blob: cbbcfbaea79264b3653540283dc91e056cc5ee18 [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 ****************************************************************/
Rich Lanec0e20ff2013-12-15 23:40:31 -0800427
428/**
429 * Generic experimenter type value. Applies to all except
430 * top level message: Action, instruction, error, stats, queue_props, oxm
431 */
432#define OF_EXPERIMENTER_TYPE 0xffff
433
434int of_experimenter_stats_request_to_object_id(uint32_t experimenter, uint32_t subtype, int ver);
435int of_experimenter_stats_reply_to_object_id(uint32_t experimenter, uint32_t subtype, int ver);
436
437of_object_id_t of_action_to_object_id(int action, of_version_t version);
438of_object_id_t of_action_id_to_object_id(int action_id, of_version_t version);
439of_object_id_t of_instruction_to_object_id(int instruction, of_version_t version);
440of_object_id_t of_queue_prop_to_object_id(int queue_prop, of_version_t version);
441of_object_id_t of_table_feature_prop_to_object_id(int table_feature_prop, of_version_t version);
442of_object_id_t of_meter_band_to_object_id(int meter_band, of_version_t version);
443of_object_id_t of_hello_elem_to_object_id(int hello_elem, of_version_t version);
444of_object_id_t of_stats_reply_to_object_id(int stats_reply, of_version_t version);
445of_object_id_t of_stats_request_to_object_id(int stats_request, of_version_t version);
446of_object_id_t of_error_msg_to_object_id(uint16_t error_msg, of_version_t version);
447of_object_id_t of_flow_mod_to_object_id(int flow_mod, of_version_t version);
448of_object_id_t of_group_mod_to_object_id(int group_mod, of_version_t version);
449of_object_id_t of_oxm_to_object_id(uint32_t type_len, of_version_t version);
450of_object_id_t of_message_experimenter_to_object_id(of_message_t msg, of_version_t version);
451of_object_id_t of_message_to_object_id(of_message_t msg, int length);
Rich Lane713d9282013-12-30 15:21:35 -0800452of_object_id_t of_bsn_tlv_to_object_id(int tlv_type, of_version_t version);
Rich Lanec0e20ff2013-12-15 23:40:31 -0800453
454int of_object_wire_init(of_object_t *obj, of_object_id_t base_object_id, int max_len);
455
456extern const int *const of_object_fixed_len[OF_VERSION_ARRAY_MAX];
457extern const int *const of_object_extra_len[OF_VERSION_ARRAY_MAX];
Rich Lanea06d0c32013-03-25 08:52:03 -0700458""")
Rich Lanea06d0c32013-03-25 08:52:03 -0700459 c_type_maps.gen_type_data_header(out)
460 c_match.gen_declarations(out)
461 # @fixme Move debug stuff to own fn
462 out.write("""
463/**
464 * Macro to check consistency of length for top level objects
465 *
466 * If the object has no parent then its length should match the
467 * underlying wire buffer's current bytes.
468 */
469#define OF_LENGTH_CHECK_ASSERT(obj) \\
Rich Lanee57f0432014-02-19 10:31:53 -0800470 LOCI_ASSERT(((obj)->parent != NULL) || \\
Rich Lanea06d0c32013-03-25 08:52:03 -0700471 ((obj)->wire_object.wbuf == NULL) || \\
472 (WBUF_CURRENT_BYTES((obj)->wire_object.wbuf) == (obj)->length))
473
474#define OF_DEBUG_DUMP
475#if defined(OF_DEBUG_DUMP)
476extern void dump_match(of_match_t *match);
477#endif /* OF_DEBUG_DUMP */
478""")
479
480 out.write("\n#endif /* Top header file */\n")
481
482def match_c_gen(out, name):
483 """
484 Generate code for
485 @param out The file handle to write to
486 @param name The name of the file
487 """
488 c_match.match_c_top_matter(out, name)
489 c_match.gen_match_conversions(out)
490 c_match.gen_serialize(out)
491 c_match.gen_deserialize(out)
492
Rich Lanea06d0c32013-03-25 08:52:03 -0700493################################################################
494# Top Matter
495################################################################
496
497def common_top_matter(out, name):
498 loxi_utils.gen_c_copy_license(out)
499 out.write("""\
Rich Laned983aa52013-06-13 11:48:37 -0700500
Rich Lanea06d0c32013-03-25 08:52:03 -0700501/****************************************************************
502 * File: %s
503 *
504 * DO NOT EDIT
505 *
506 * This file is automatically generated
507 *
508 ****************************************************************/
509
510""" % name)
511
512 if name[-2:] == ".h":
513 out.write("""
514#if !defined(%(h)s)
515#define %(h)s
516
517""" % dict(h=h_file_to_define(name)))
518
519def base_h_content(out):
520 """
521 Generate base header file content
522
523 @param out The output file object
524 """
525
526 # @fixme Supported version should be generated based on input to LoxiGen
527
528 out.write("""
529/*
530 * Base OpenFlow definitions. These depend only on standard C headers
531 */
532#include <string.h>
533#include <stdint.h>
534
535/* g++ requires this to pick up PRI, etc.
536 * See http://gcc.gnu.org/ml/gcc-help/2006-10/msg00223.html
537 */
538#if !defined(__STDC_FORMAT_MACROS)
539#define __STDC_FORMAT_MACROS
540#endif
541#include <inttypes.h>
542
543#include <stdlib.h>
544#include <assert.h>
545#include <loci/loci_idents.h>
546
547/**
548 * Macro to enable debugging for LOCI.
549 *
550 * This enables debug output to stdout.
551 */
552#define OF_DEBUG_ENABLE
553
554#if defined(OF_DEBUG_ENABLE)
555#include <stdio.h> /* Currently for debugging */
556#define FIXME(str) do { \\
557 fprintf(stderr, "%s\\n", str); \\
558 exit(1); \\
559 } while (0)
560#define debug printf
561#else
562#define FIXME(str)
563#define debug(str, ...)
564#endif /* OF_DEBUG_ENABLE */
565
566/**
567 * The type of a function used by the LOCI dump/show functions to
568 * output text. Essentially the same signature as fprintf. May
569 * be called many times per invocation of e.g. of_object_show().
570 */
571typedef int (*loci_writer_f)(void *cookie, const char *fmt, ...);
572
573/**
574 * Check if a version is supported
575 */
576#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_3)
577
578""")
579 gen_version_enum(out)
580 out.write("\n")
581
582 # for c_name in of_g.ofp_constants:
583 # val = str(of_g.ofp_constants[c_name])
584 # out.write("#define %s %s\n" % (c_name, val))
585 # out.write("\n")
586
587 out.write("""
588typedef enum of_error_codes_e {
589 OF_ERROR_NONE = 0,
590 OF_ERROR_RESOURCE = -1, /* Could not allocate space */
591 OF_ERROR_PARAM = -2, /* Bad parameter */
592 OF_ERROR_VERSION = -3, /* Version not supported */
593 OF_ERROR_RANGE = -4, /* End of list indication */
594 OF_ERROR_COMPAT = -5, /* Incompatible assignment */
595 OF_ERROR_PARSE = -6, /* Error in parsing data */
596 OF_ERROR_INIT = -7, /* Uninitialized data */
597 OF_ERROR_UNKNOWN = -8 /* Unknown error */
598} of_error_codes_t;
599
600#define OF_ERROR_STRINGS "none", \\
601 "resource", \\
602 "parameter", \\
603 "version", \\
604 "range", \\
605 "incompatible", \\
606 "parse", \\
607 "init", \\
608 "unknown"
609
Rich Laneb157b0f2013-03-27 13:55:28 -0700610extern const char *const of_error_strings[];
Rich Lanea06d0c32013-03-25 08:52:03 -0700611
Rich Lanea8b54632014-02-19 11:17:47 -0800612#ifdef __GNUC__
613#define LOCI_NORETURN_ATTR __attribute__((__noreturn__))
614#else
615#define LOCI_NORETURN_ATTR
616#endif
617
618extern void loci_assert_fail(
619 const char *cond,
620 const char *file,
621 unsigned int line) LOCI_NORETURN_ATTR;
622
Rich Lane53757732013-02-23 17:00:10 -0800623#ifndef NDEBUG
Rich Lanea8b54632014-02-19 11:17:47 -0800624#define LOCI_ASSERT(val) ((val) ? (void)0 : loci_assert_fail(#val, __FILE__, __LINE__))
Rich Lane53757732013-02-23 17:00:10 -0800625#else
Rich Lanee57f0432014-02-19 10:31:53 -0800626#define LOCI_ASSERT(val)
Rich Lane53757732013-02-23 17:00:10 -0800627#endif
Rich Lanea06d0c32013-03-25 08:52:03 -0700628
629/*
630 * Some LOCI object accessors can fail, and it's easy to forget to check.
631 * On certain compilers we can trigger a warning if the error code
632 * is ignored.
633 */
634#ifndef DISABLE_WARN_UNUSED_RESULT
635#ifdef __GNUC__
636#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
637#else
638#define WARN_UNUSED_RESULT
639#endif
640#else
641#define WARN_UNUSED_RESULT
642#endif
643
644typedef union of_generic_u of_generic_t;
645typedef struct of_object_s of_object_t;
646
647/* Define ipv4 address as uint32 */
648typedef uint32_t of_ipv4_t;
649
650/* Table ID is the OF standard uint8 */
651typedef uint8_t of_table_id_t;
652
653#define OF_MAC_ADDR_BYTES 6
654typedef struct of_mac_addr_s {
655 uint8_t addr[OF_MAC_ADDR_BYTES];
656} of_mac_addr_t;
657
658#define OF_IPV6_BYTES 16
659typedef struct of_ipv6_s {
660 uint8_t addr[OF_IPV6_BYTES];
661} of_ipv6_t;
662
663extern const of_mac_addr_t of_mac_addr_all_ones;
664extern const of_mac_addr_t of_mac_addr_all_zeros;
665
666extern const of_ipv6_t of_ipv6_all_ones;
667extern const of_ipv6_t of_ipv6_all_zeros;
668
669/**
670 * Generic zero and all-ones values of size 16 bytes.
671 *
672 * IPv6 is longest data type we worry about for comparisons
673 */
674#define of_all_zero_value of_ipv6_all_zeros
675#define of_all_ones_value of_ipv6_all_ones
676
677/**
678 * Non-zero/all ones check for arbitrary type of size <= 16 bytes
679 */
680#define OF_VARIABLE_IS_NON_ZERO(_ptr) \\
681 (MEMCMP(&of_all_zero_value, (_ptr), sizeof(*(_ptr))))
682#define OF_VARIABLE_IS_ALL_ONES(_ptr) \\
683 (!MEMCMP(&of_all_ones_value, (_ptr), sizeof(*(_ptr))))
684
685/* The octets object is a struct holding pointer and length */
686typedef struct of_octets_s {
687 uint8_t *data;
688 int bytes;
689} of_octets_t;
690
691/* Macro to convert an octet object to a pointer; currently trivial */
692#define OF_OCTETS_POINTER_GET(octet_ptr) ((octet_ptr)->data)
693#define OF_OCTETS_POINTER_SET(octet_ptr, ptr) (octet_ptr)->data = (ptr)
694#define OF_OCTETS_BYTES_GET(octet_ptr) ((octet_ptr)->bytes)
695#define OF_OCTETS_BYTES_SET(octet_ptr, bytes) (octet_ptr)->bytes = (bytes)
696
697/* Currently these are categorized as scalars */
698typedef char of_port_name_t[OF_MAX_PORT_NAME_LEN];
699typedef char of_table_name_t[OF_MAX_TABLE_NAME_LEN];
700typedef char of_desc_str_t[OF_DESC_STR_LEN];
701typedef char of_serial_num_t[OF_SERIAL_NUM_LEN];
702
Rich Lane3b2fd832013-09-24 13:44:08 -0700703typedef struct of_bitmap_128_s {
704 uint64_t hi;
705 uint64_t lo;
706} of_bitmap_128_t;
707
Rich Lanefab0c822013-12-30 11:46:48 -0800708typedef struct of_checksum_128_s {
709 uint64_t hi;
710 uint64_t lo;
711} of_checksum_128_t;
712
Rich Lanea06d0c32013-03-25 08:52:03 -0700713/* These are types which change across versions. */
714typedef uint32_t of_port_no_t;
715typedef uint16_t of_fm_cmd_t;
716typedef uint64_t of_wc_bmap_t;
717typedef uint64_t of_match_bmap_t;
718
719#define MEMMOVE(dest, src, bytes) memmove(dest, src, bytes)
720#define MEMSET(dest, val, bytes) memset(dest, val, bytes)
721#define MEMCPY(dest, src, bytes) memcpy(dest, src, bytes)
722#define MEMCMP(a, b, bytes) memcmp(a, b, bytes)
723#define MALLOC(bytes) malloc(bytes)
724#define FREE(ptr) free(ptr)
725
726/** Try an operation and return on failure. */
727#define OF_TRY(op) do { \\
728 int _rv; \\
729 if ((_rv = (op)) < 0) { \\
730 LOCI_LOG_ERROR("ERROR %d at %s:%d\\n", _rv, __FILE__, __LINE__); \\
731 return _rv; \\
732 } \\
733 } while (0)
734
735/* The extent of an OF match object is determined by its length field, but
736 * aligned to 8 bytes
737 */
738
739#define OF_MATCH_BYTES(length) (((length) + 7) & 0xfff8)
740
741#if __BYTE_ORDER == __BIG_ENDIAN
742#define U16_NTOH(val) (val)
743#define U32_NTOH(val) (val)
744#define U64_NTOH(val) (val)
745#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
746#define U16_HTON(val) (val)
747#define U32_HTON(val) (val)
748#define U64_HTON(val) (val)
749#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
750#else /* Little Endian */
751#define U16_NTOH(val) (((val) >> 8) | ((val) << 8))
752#define U32_NTOH(val) ((((val) & 0xff000000) >> 24) | \\
753 (((val) & 0x00ff0000) >> 8) | \\
754 (((val) & 0x0000ff00) << 8) | \\
755 (((val) & 0x000000ff) << 24))
756#define U64_NTOH(val) ((((val) & 0xff00000000000000LL) >> 56) | \\
757 (((val) & 0x00ff000000000000LL) >> 40) | \\
758 (((val) & 0x0000ff0000000000LL) >> 24) | \\
759 (((val) & 0x000000ff00000000LL) >> 8) | \\
760 (((val) & 0x00000000ff000000LL) << 8) | \\
761 (((val) & 0x0000000000ff0000LL) << 24) | \\
762 (((val) & 0x000000000000ff00LL) << 40) | \\
763 (((val) & 0x00000000000000ffLL) << 56))
764#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
765#define U16_HTON(val) U16_NTOH(val)
766#define U32_HTON(val) U32_NTOH(val)
767#define U64_HTON(val) U64_NTOH(val)
768#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
769#endif
770
771/****************************************************************
772 *
773 * The following are internal definitions used by the automatically
774 * generated code. Users should not reference these definitions
775 * as they may change between versions of this code
776 *
777 ****************************************************************/
778
779#define OF_MESSAGE_IN_MATCH_POINTER(obj) \\
780 (WIRE_BUF_POINTER(&((obj)->wire_buffer), OF_MESSAGE_IN_MATCH_OFFSET))
781#define OF_MESSAGE_IN_MATCH_LEN(ptr) BUF_U16_GET(&ptr[2])
782#define OF_MESSAGE_IN_DATA_OFFSET(obj) \\
783 (FIXED_LEN + OF_MESSAGE_IN_MATCH_LEN(OF_MESSAGE_IN_MATCH_POINTER(obj)) + 2)
784
785#define OF_MESSAGE_OUT_DATA_OFFSET(obj) \\
786 (FIXED_LEN + of_message_out_actions_len_get(obj))
787
788""")
789
790def external_h_top_matter(out, name):
791 """
792 Generate top matter for external header file
793
794 @param name The name of the output file
795 @param out The output file object
796 """
797 common_top_matter(out, name)
798 out.write("""
799#include <loci/loci_base.h>
800#include <loci/of_message.h>
801#include <loci/of_match.h>
802#include <loci/of_object.h>
Rich Lanedef2e512013-12-15 15:54:02 -0800803#include <loci/loci_classes.h>
Rich Lanea06d0c32013-03-25 08:52:03 -0700804
805/****************************************************************
806 *
807 * This file is divided into the following sections.
808 *
809 * A few object specific macros
810 * Class typedefs (no struct definitions)
811 * Per-data type accessor function typedefs
812 * Per-class new/delete function typedefs
813 * Per-class static delete functions
814 * Per-class, per-member accessor declarations
815 * Per-class structure definitions
816 * Generic union (inheritance) definitions
817 * Pointer set function declarations
818 * Some special case macros
819 *
820 ****************************************************************/
821""")
822
Rich Lanea06d0c32013-03-25 08:52:03 -0700823################################################################
824#
825################################################################
826
827def gen_version_enum(out):
828 """
829 Generate the enumerated type for versions in LoxiGen
830 @param out The file object to which to write the decs
831
832 This just uses the wire versions for now
833 """
834 out.write("""
835/**
836 * Enumeration of OpenFlow versions
837 *
838 * The wire protocol numbers are currently used for values of the corresponding
839 * version identifiers.
840 */
841typedef enum of_version_e {
842 OF_VERSION_UNKNOWN = 0,
843""")
844
845 is_first = True
846 max = 0
847 for v in of_g.wire_ver_map:
848 if is_first:
849 is_first = False
850 else:
851 out.write(",\n")
852 if v > max:
853 max = v
854 out.write(" %s = %d" % (of_g.wire_ver_map[v], v))
855
856 out.write("""
857} of_version_t;
858
859/**
860 * @brief Use this when declaring arrays indexed by wire version
861 */
862#define OF_VERSION_ARRAY_MAX %d
863""" % (max + 1))
Andreas Wundsam53256162013-05-02 14:05:53 -0700864
Rich Lanea06d0c32013-03-25 08:52:03 -0700865def gen_object_enum(out):
866 """
867 Generate the enumerated type for object identification in LoxiGen
868 @param out The file object to which to write the decs
869 """
870 out.write("""
871
872/**
873 * Enumeration of OpenFlow objects
874 *
875 * We enumerate the OpenFlow objects used internally. Note that some
876 * message types are determined both by an outer type (message type like
877 * stats_request) and an inner type (port stats). These are different
878 * messages in ofC.
879 *
880 * These values are for internal use only. They will change with
881 * different versions of ofC.
882 */
883
884typedef enum of_object_id_e {
885 /* Root object type */
886 OF_OBJECT_INVALID = -1, /* "invalid" return value for mappings */
887 OF_OBJECT = 0, /* Generic, untyped object */
888
889 /* OpenFlow message objects */
890""")
891 last = 0
892 msg_count = 0
893 for cls in of_g.ordered_messages:
894 out.write(" %s = %d,\n" % (enum_name(cls),
895 of_g.unified[cls]["object_id"]))
896 msg_count = of_g.unified[cls]["object_id"] + 1
897
898 out.write("\n /* Non-message objects */\n")
899 for cls in of_g.ordered_non_messages:
900 out.write(" %s = %d,\n" % (enum_name(cls),
901 of_g.unified[cls]["object_id"]))
902 last = of_g.unified[cls]["object_id"]
903 out.write("\n /* List objects */\n")
904 for cls in of_g.ordered_list_objects:
905 out.write(" %s = %d,\n" % (enum_name(cls),
906 of_g.unified[cls]["object_id"]))
907 last = of_g.unified[cls]["object_id"]
908
909 out.write("\n /* Generic stats request/reply types; pseudo objects */\n")
910 for cls in of_g.ordered_pseudo_objects:
911 out.write(" %s = %d,\n" % (enum_name(cls),
912 of_g.unified[cls]["object_id"]))
913 last = of_g.unified[cls]["object_id"]
914
915 out.write("""
916 OF_OBJECT_COUNT = %d
917} of_object_id_t;
918
Rich Laneb157b0f2013-03-27 13:55:28 -0700919extern const char *const of_object_id_str[];
Rich Lanea06d0c32013-03-25 08:52:03 -0700920
921#define OF_MESSAGE_OBJECT_COUNT %d
922""" % ((last + 1), msg_count))
923
924 # Generate object type range checking for inheritance classes
925
926 # @fixme These should be determined algorithmicly
927 out.write("""
928/*
929 * Macros to check if an object ID is within an inheritance class range
930 */
931""")
932 # Alphabetical order for 'last'
933 last_ids = dict(of_action="OF_ACTION_STRIP_VLAN",
934 of_oxm="OF_OXM_VLAN_VID_MASKED",
935 of_instruction="OF_INSTRUCTION_WRITE_METADATA",
936 of_queue_prop="OF_QUEUE_PROP_MIN_RATE",
937 of_table_feature_prop="OF_TABLE_FEATURE_PROP_WRITE_SETFIELD_MISS",
938 # @FIXME add meter_band ?
939 )
940 for cls, last in last_ids.items():
941 out.write("""
942#define %(enum)s_FIRST_ID (%(enum)s + 1)
943#define %(enum)s_LAST_ID %(last)s
944#define %(enum)s_VALID_ID(id) \\
945 ((id) >= %(enum)s_FIRST_ID && \\
946 (id) <= %(enum)s_LAST_ID)
947""" % dict(enum=enum_name(cls), last=last))
948 out.write("""
949/**
950 * Function to check a wire ID
951 * @param object_id The ID to check
952 * @param base_object_id The inheritance parent, if applicable
953 * @returns boolean: If base_object_id is an inheritance class, check if
954 * object_id is valid as a subclass. Otherwise return 1.
955 *
956 * Note: Could check that object_id == base_object_id in the
957 * second case.
958 */
959static inline int
960of_wire_id_valid(int object_id, int base_object_id) {
961 switch (base_object_id) {
962 case OF_ACTION:
963 return OF_ACTION_VALID_ID(object_id);
964 case OF_OXM:
965 return OF_OXM_VALID_ID(object_id);
966 case OF_QUEUE_PROP:
967 return OF_QUEUE_PROP_VALID_ID(object_id);
968 case OF_TABLE_FEATURE_PROP:
969 return OF_TABLE_FEATURE_PROP_VALID_ID(object_id);
970 case OF_INSTRUCTION:
971 return OF_INSTRUCTION_VALID_ID(object_id);
972 default:
973 break;
974 }
975 return 1;
976}
977""")
978
Rich Lanea06d0c32013-03-25 08:52:03 -0700979################################################################
980#
981# Internal Utility Functions
982#
983################################################################
984
985
986def acc_name(cls, m_name):
987 """
988 Generate the root name of an accessor function for typedef
989 @param cls The class name
990 @param m_name The member name
991 """
992 (m_type, get_rv) = get_acc_rv(cls, m_name)
993 return "%s_%s" % (cls, m_type)
994
995def get_acc_rv(cls, m_name):
996 """
997 Determine the data type and return type for a get accessor.
998
999 The return type may be "void" or it may be the accessor type
1000 depending on the system configuration and on the data type.
1001
1002 @param cls The class name
1003 @param m_name The member name
1004 @return A pair (m_type, rv) where m_type is the unified type of the
1005 member and rv is the get_accessor return type
1006 """
1007 member = of_g.unified[cls]["union"][m_name]
1008 m_type = member["m_type"]
1009 rv = "int"
Rich Lanea06d0c32013-03-25 08:52:03 -07001010 if m_type[-2:] == "_t":
1011 m_type = m_type[:-2]
1012
1013 return (m_type, rv)
1014
1015def param_list(cls, m_name, a_type):
1016 """
1017 Generate the parameter list (no parens) for an a_type accessor
1018 @param cls The class name
1019 @param m_name The member name
1020 @param a_type One of "set" or "get" or TBD
1021 """
1022 member = of_g.unified[cls]["union"][m_name]
1023 m_type = member["m_type"]
1024 params = ["%s_t *obj" % cls]
1025 if a_type == "set":
1026 if loxi_utils.type_is_scalar(m_type):
1027 params.append("%s %s" % (m_type, m_name))
1028 else:
1029 params.append("%s *%s" % (m_type, m_name))
1030 elif a_type in ["get", "bind"]:
1031 params.append("%s *%s" % (m_type, m_name))
1032 else:
1033 debug("Class %s, name %s Bad param list a_type: %s" %
1034 (cls, m_name, a_type))
1035 sys.exit(1)
1036 return params
1037
1038def typed_function_base(cls, m_name):
1039 """
1040 Generate the core name for accessors based on the type
1041 @param cls The class name
1042 @param m_name The member name
1043 """
1044 (m_type, get_rv) = get_acc_rv(cls, m_name)
1045 return "%s_%s" % (cls, m_type)
1046
1047def member_function_base(cls, m_name):
1048 """
1049 Generate the core name for accessors based on the member name
1050 @param cls The class name
1051 @param m_name The member name
1052 """
1053 return "%s_%s" % (cls, m_name)
1054
1055def field_ver_get(cls, m_name):
1056 """
1057 Generate a dict, indexed by wire version, giving a pair (type, offset)
1058
1059 @param cls The class name
1060 @param m_name The name of the class member
1061
1062 If offset is not known for m_name, the type
1063 A dict is used for more convenient indexing.
1064 """
1065 result = {}
1066
1067 for ver in of_g.unified[cls]:
1068 if type(ver) == type(0): # It's a version
1069 if "use_version" in of_g.unified[cls][ver]: # deref version ref
1070 ref_ver = of_g.unified[cls][ver]["use_version"]
1071 members = of_g.unified[cls][ref_ver]["members"]
1072 else:
1073 members = of_g.unified[cls][ver]["members"]
1074 idx = loxi_utils.member_to_index(m_name, members)
1075 if (idx < 0):
1076 continue # Member not in this version
1077 m_type = members[idx]["m_type"]
1078 offset = members[idx]["offset"]
1079
1080 # If m_type is mixed, get wire version type from global data
1081 if m_type in of_g.of_mixed_types and \
1082 ver in of_g.of_mixed_types[m_type]:
1083 m_type = of_g.of_mixed_types[m_type][ver]
1084
1085 # add version to result list
1086 result[ver] = (m_type, offset)
1087
1088 return result
1089
1090def v3_match_offset_get(cls):
1091 """
Andreas Wundsam53256162013-05-02 14:05:53 -07001092 Return the offset of an OF 1.2 match in an object if it has such;
Rich Lanea06d0c32013-03-25 08:52:03 -07001093 otherwise return -1
1094 """
1095 result = field_ver_get(cls, "match")
1096 if of_g.VERSION_1_2 in result:
1097 if result[of_g.VERSION_1_2][0] == "of_match_v3_t":
1098 return result[of_g.VERSION_1_2][1]
1099 return -1
1100
1101################################################################
1102#
1103# OpenFlow Object Definitions
1104#
1105################################################################
1106
1107
1108def gen_of_object_defs(out):
1109 """
1110 Generate low level of_object core operations
1111 @param out The file for output, already open
1112 """
1113
1114def gen_generics(out):
1115 for (cls, subclasses) in type_maps.inheritance_map.items():
1116 out.write("""
1117/**
1118 * Inheritance super class for %(cls)s
1119 *
1120 * This class is the union of %(cls)s classes. You can refer
1121 * to it untyped by refering to the member 'header' whose structure
1122 * is common across all sub-classes.
1123 */
1124
1125union %(cls)s_u {
1126 %(cls)s_header_t header; /* Generic instance */
1127""" % dict(cls=cls))
1128 for subcls in sorted(subclasses):
1129 out.write(" %s_%s_t %s;\n" % (cls, subcls, subcls))
1130 out.write("};\n")
1131
1132def gen_struct_typedefs(out):
1133 """
1134 Generate typedefs for all struct objects
1135 @param out The file for output, already open
1136 """
1137
1138 out.write("\n/* LOCI inheritance parent typedefs */\n")
1139 for cls in type_maps.inheritance_map:
1140 out.write("typedef union %(cls)s_u %(cls)s_t;\n" % dict(cls=cls))
1141 out.write("\n/* LOCI object typedefs */\n")
1142 for cls in of_g.standard_class_order:
1143 if cls in type_maps.inheritance_map:
1144 continue
Rich Lane85767872013-12-15 16:24:42 -08001145 template = "typedef of_object_t %(cls)s_t;\n"
1146 out.write(template % dict(cls=cls))
Rich Lanea06d0c32013-03-25 08:52:03 -07001147
1148 out.write("""
1149/****************************************************************
1150 *
1151 * Additional of_object defines
1152 * These are needed for some static inline ops, so we put them here.
1153 *
1154 ****************************************************************/
1155
1156/* Delete an OpenFlow object without reference to its type */
1157extern void of_object_delete(of_object_t *obj);
1158
1159""")
1160
Rich Lanea06d0c32013-03-25 08:52:03 -07001161def gen_flow_add_setup_function_declarations(out):
1162 """
1163 Add the declarations for functions that can be initialized
1164 by a flow add. These are defined external to LOXI.
1165 """
1166
1167 out.write("""
1168/****************************************************************
1169 * Functions for objects that can be initialized by a flow add message.
1170 * These are defined in a non-autogenerated file
1171 ****************************************************************/
1172
1173/**
1174 * @brief Set up a flow removed message from the original add
1175 * @param obj The flow removed message being updated
1176 * @param flow_add The flow_add message to use
1177 *
1178 * Initialize the following fields of obj to be identical
1179 * to what was originally on the wire from the flow_add object:
1180 * match
1181 * cookie
1182 * priority
1183 * idle_timeout
1184 * hard_timeout
1185 *
1186 */
1187
1188extern int
1189of_flow_removed_setup_from_flow_add(of_flow_removed_t *obj,
1190 of_flow_add_t *flow_add);
1191
1192
1193/**
1194 * @brief Set up the packet in match structure from the original add
1195 * @param obj The packet in message being updated
1196 * @param flow_add The flow_add message to use
1197 * @returns Indigo error code. Does not return a version error if
1198 * the version does not require initializing obj.
1199 *
1200 * Initialize the match member of obj to be identical to what was originally
1201 * on the wire from the flow_add object. If applicable, the table ID is also
1202 * initialized from the flow_add object.
1203 *
1204 * This API applies to 1.2 and later only.
1205 */
1206
1207extern int
1208of_packet_in_setup_from_flow_add(of_packet_in_t *obj,
1209 of_flow_add_t *flow_add);
1210
1211
1212/**
1213 * @brief Set up the flow stats entry from the original add
1214 * @param obj The packet in message being updated
1215 * @param flow_add The flow_add message to use
1216 * @param effects Optional actions or instructions; see below.
1217 *
1218 * Initialize the following fields of obj to be identical
1219 * to what was originally on the wire from the flow_add object:
1220 * match
1221 * actions/instructions (effects)
1222 * cookie
1223 * priority
1224 * idle_timeout
1225 * hard_timeout
1226 *
Andreas Wundsam53256162013-05-02 14:05:53 -07001227 * Note that the actions/instructions of a flow may be modified by a
Rich Lanea06d0c32013-03-25 08:52:03 -07001228 * subsequent flow modify message. To facilitate implementations,
1229 * the "effects" parameter is provided. If effects is NULL, the
1230 * actions/instructions are taken from the flow_add message.
1231 * Otherwise, effects is coerced to the proper type (actions or
1232 * instructions) and used to init obj.
1233 */
1234
1235extern int
1236of_flow_stats_entry_setup_from_flow_add(of_flow_stats_entry_t *obj,
1237 of_flow_add_t *flow_add,
1238 of_object_t *effects);
1239""")
1240
Rich Lanea06d0c32013-03-25 08:52:03 -07001241################################################################
1242#
1243# List accessor code generation
1244#
1245# Currently these all implement copy on read semantics
1246#
1247################################################################
1248
1249def init_call(e_type, obj, ver, length, cw):
1250 """
1251 Generate the init call given the strings for params
1252 """
1253 hdr = "" # If inheritance type, coerce to hdr object
1254 obj_name = obj
1255 if e_type in type_maps.inheritance_map:
1256 hdr = "_header"
1257 obj_name = "(%s_header_t *)" % e_type + obj
1258
1259 return """\
1260%(e_type)s%(hdr)s_init(%(obj_name)s,
1261 %(ver)s, %(length)s, %(cw)s)\
1262""" % dict(e_type=e_type, hdr=hdr, obj_name=obj_name, ver=ver,
1263 length=length, cw=cw)
1264
1265def gen_list_first(out, cls, e_type):
1266 """
1267 Generate the body of a list_first operation
1268 @param cls The class name for which code is being generated
1269 @param e_type The element type of the list
1270 @param out The file to which to write
1271 """
1272 i_call = init_call(e_type, "obj", "list->version", "0", "1")
1273 if e_type in type_maps.inheritance_map:
1274 len_str = "obj->header.length"
1275 else:
1276 len_str = "obj->length"
1277
1278 out.write("""
1279/**
1280 * Associate an iterator with a list
1281 * @param list The list to iterate over
1282 * @param obj The list entry iteration pointer
1283 * @return OF_ERROR_RANGE if the list is empty (end of list)
1284 *
1285 * The obj instance is completely initialized. The caller is responsible
1286 * for cleaning up any wire buffers associated with obj before this call
1287 */
1288
1289int
1290%(cls)s_first(%(cls)s_t *list,
1291 %(e_type)s_t *obj)
1292{
1293 int rv;
1294
1295 %(i_call)s;
1296 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1297 return rv;
1298 }
1299""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1300
1301 # Special case flow_stats_entry lists
1302
1303 out.write("""
1304 of_object_wire_init((of_object_t *) obj, %(u_type)s,
1305 list->length);
1306 if (%(len_str)s == 0) {
1307 return OF_ERROR_PARSE;
1308 }
1309
1310 return rv;
1311}
1312""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1313
1314
1315def gen_bind(out, cls, m_name, m_type):
1316 """
1317 Generate the body of a bind function
1318 @param out The file to which to write
1319 @param cls The class name for which code is being generated
1320 @param m_name The name of the data member
1321 @param m_type The type of the data member
1322 """
1323
1324 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1325
1326 i_call = init_call(e_type, "child", "parent->version", "0", "1")
1327
1328 out.write("""
1329/**
1330 * Bind the child object to the parent object for read processing
1331 * @param parent The parent object
1332 * @param child The child object
1333 *
1334 * The child obj instance is completely initialized.
1335 */
1336
1337int
1338%(cls)s_%(m_name)_bind(%(cls)s_t *parent,
1339 %(e_type)s_t *child)
1340{
1341 int rv;
1342
1343 %(i_call)s;
1344
1345 /* Derive offset and length of child in parent */
Andreas Wundsam53256162013-05-02 14:05:53 -07001346 OF_TRY(of_object_child_attach(parent, child,
Rich Lanea06d0c32013-03-25 08:52:03 -07001347 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1348 return rv;
1349 }
1350""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1351
1352 # Special case flow_stats_entry lists
1353
1354 out.write("""
1355 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1356 list->length);
1357 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1358 return OF_ERROR_PARSE;
1359 }
1360
1361 return rv;
1362}
1363""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1364
1365
1366def gen_list_next(out, cls, e_type):
1367 """
1368 Generate the body of a list_next operation
1369 @param cls The class name for which code is being generated
1370 @param e_type The element type of the list
1371 @param out The file to which to write
1372 """
1373
1374 if e_type in type_maps.inheritance_map:
1375 len_str = "obj->header.length"
1376 else:
1377 len_str = "obj->length"
Andreas Wundsam53256162013-05-02 14:05:53 -07001378
Rich Lanea06d0c32013-03-25 08:52:03 -07001379 out.write("""
1380/**
1381 * Advance an iterator to the next element in a list
1382 * @param list The list being iterated
1383 * @param obj The list entry iteration pointer
1384 * @return OF_ERROR_RANGE if already at the last entry on the list
1385 *
1386 */
1387
1388int
1389%(cls)s_next(%(cls)s_t *list,
1390 %(e_type)s_t *obj)
1391{
1392 int rv;
1393
1394 if ((rv = of_list_next((of_object_t *)list, (of_object_t *)obj)) < 0) {
1395 return rv;
1396 }
1397
1398 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1399 list->length);
1400
1401 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1402 return OF_ERROR_PARSE;
1403 }
1404
1405 return rv;
1406}
1407""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1408
1409def gen_list_append(out, cls, e_type):
1410 """
1411 Generate the body of a list append functions
1412 @param cls The class name for which code is being generated
1413 @param e_type The element type of the list
1414 @param out The file to which to write
1415 """
1416
1417 out.write("""
1418/**
1419 * Set up to append an object of type %(e_type)s to an %(cls)s.
1420 * @param list The list that is prepared for append
1421 * @param obj Pointer to object to hold data to append
1422 *
1423 * The obj instance is completely initialized. The caller is responsible
1424 * for cleaning up any wire buffers associated with obj before this call.
1425 *
1426 * See the generic documentation for of_list_append_bind.
1427 */
1428
1429int
1430%(cls)s_append_bind(%(cls)s_t *list,
1431 %(e_type)s_t *obj)
1432{
1433 return of_list_append_bind((of_object_t *)list, (of_object_t *)obj);
1434}
1435
1436/**
1437 * Append an item to a %(cls)s list.
1438 *
1439 * This copies data from item and leaves item untouched.
1440 *
1441 * See the generic documentation for of_list_append.
1442 */
1443
1444int
1445%(cls)s_append(%(cls)s_t *list,
1446 %(e_type)s_t *item)
1447{
1448 return of_list_append((of_object_t *)list, (of_object_t *)item);
1449}
1450
1451""" % dict(cls=cls, e_type=e_type))
1452
1453def gen_list_accessors(out, cls):
1454 e_type = loxi_utils.list_to_entry_type(cls)
1455 gen_list_first(out, cls, e_type)
1456 gen_list_next(out, cls, e_type)
1457 gen_list_append(out, cls, e_type)
1458
1459################################################################
1460#
1461# Accessor Functions
1462#
1463################################################################
1464
Andreas Wundsam53256162013-05-02 14:05:53 -07001465
Rich Lanea06d0c32013-03-25 08:52:03 -07001466def gen_accessor_declarations(out):
1467 """
1468 Generate the declaration of each version independent accessor
1469
1470 @param out The file to which to write the decs
1471 """
1472
1473 out.write("""
1474/****************************************************************
1475 *
1476 * Unified, per-member accessor function declarations
1477 *
1478 ****************************************************************/
1479""")
1480 for cls in of_g.standard_class_order:
1481 if cls in type_maps.inheritance_map:
1482 continue
1483 out.write("\n/* Unified accessor functions for %s */\n" % cls)
1484 for m_name in of_g.ordered_members[cls]:
1485 if m_name in of_g.skip_members:
1486 continue
1487 m_type = loxi_utils.member_base_type(cls, m_name)
1488 base_name = "%s_%s" % (cls, m_name)
1489 gparams = ",\n ".join(param_list(cls, m_name, "get"))
1490 get_ret_type = accessor_return_type("get", m_type)
1491 sparams = ",\n ".join(param_list(cls, m_name, "set"))
1492 set_ret_type = accessor_return_type("set", m_type)
1493 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1494 bind_ret_type = accessor_return_type("bind", m_type)
1495
1496 if loxi_utils.type_is_of_object(m_type):
1497 # Generate bind accessors, but not get accessor
1498 out.write("""
1499extern %(set_ret_type)s %(base_name)s_set(
1500 %(sparams)s);
1501extern %(bind_ret_type)s %(base_name)s_bind(
1502 %(bparams)s);
1503extern %(m_type)s *%(cls)s_%(m_name)s_get(
1504 %(cls)s_t *obj);
1505""" % dict(base_name=base_name, sparams=sparams, bparams=bparams,
1506 m_name=m_name, m_type=m_type, cls=cls,
1507 set_ret_type=set_ret_type, bind_ret_type=bind_ret_type))
1508 else:
1509 out.write("""
1510extern %(set_ret_type)s %(base_name)s_set(
1511 %(sparams)s);
1512extern %(get_ret_type)s %(base_name)s_get(
1513 %(gparams)s);
1514""" % dict(base_name=base_name, gparams=gparams, sparams=sparams,
1515 get_ret_type=get_ret_type, set_ret_type=set_ret_type))
Andreas Wundsam53256162013-05-02 14:05:53 -07001516
Rich Lanea06d0c32013-03-25 08:52:03 -07001517 if loxi_utils.class_is_list(cls):
1518 e_type = loxi_utils.list_to_entry_type(cls)
1519 out.write("""
1520extern int %(cls)s_first(
1521 %(cls)s_t *list, %(e_type)s_t *obj);
1522extern int %(cls)s_next(
1523 %(cls)s_t *list, %(e_type)s_t *obj);
1524extern int %(cls)s_append_bind(
1525 %(cls)s_t *list, %(e_type)s_t *obj);
1526extern int %(cls)s_append(
1527 %(cls)s_t *list, %(e_type)s_t *obj);
1528
1529/**
1530 * Iteration macro for list of type %(cls)s
1531 * @param list Pointer to the list being iterated over of
1532 * type %(cls)s
1533 * @param elt Pointer to an element of type %(e_type)s
1534 * @param rv On exiting the loop will have the value OF_ERROR_RANGE.
1535 */
1536#define %(u_cls)s_ITER(list, elt, rv) \\
1537 for ((rv) = %(cls)s_first((list), (elt)); \\
1538 (rv) == OF_ERROR_NONE; \\
1539 (rv) = %(cls)s_next((list), (elt)))
1540""" % dict(u_cls=cls.upper(), cls=cls, e_type=e_type))
1541
1542
1543def wire_accessor(m_type, a_type):
1544 """
1545 Returns the name of the a_type accessor for low level wire buff offset
1546 @param m_type The member type
1547 @param a_type The accessor type (set or get)
1548 """
1549 # Strip off _t if present
1550 if m_type in of_g.of_base_types:
1551 m_type = of_g.of_base_types[m_type]["short_name"]
1552 if m_type in of_g.of_mixed_types:
1553 m_type = of_g.of_mixed_types[m_type]["short_name"]
1554 if m_type[-2:] == "_t":
1555 m_type = m_type[:-2]
1556 if m_type == "octets":
1557 m_type = "octets_data"
1558 return "of_wire_buffer_%s_%s" % (m_type, a_type)
1559
Rich Lane713d9282013-12-30 15:21:35 -08001560def get_len_macro(cls, m_name, m_type, version):
Rich Lanea06d0c32013-03-25 08:52:03 -07001561 """
1562 Get the length macro for m_type in cls
1563 """
1564 if m_type.find("of_match") == 0:
1565 return "_WIRE_MATCH_PADDED_LEN(obj, offset)"
1566 if m_type.find("of_list_oxm") == 0:
1567 return "wire_match_len(obj, 0) - 4"
1568 if loxi_utils.class_is_tlv16(m_type):
1569 return "_TLV16_LEN(obj, offset)"
1570 if cls == "of_packet_out" and m_type == "of_list_action_t":
1571 return "_PACKET_OUT_ACTION_LEN(obj)"
Rich Lane713d9282013-12-30 15:21:35 -08001572 if cls == "of_bsn_gentable_entry_add" and m_name == "key":
1573 return "of_object_u16_get(obj, 18)"
1574 if cls == "of_bsn_gentable_entry_desc_stats_entry" and m_name == "key":
1575 return "of_object_u16_get(obj, 2)"
1576 if cls == "of_bsn_gentable_entry_stats_entry" and m_name == "key":
1577 return "of_object_u16_get(obj, 2)"
Rich Lanea06d0c32013-03-25 08:52:03 -07001578 # Default is everything to the end of the object
1579 return "_END_LEN(obj, offset)"
1580
1581def gen_accessor_offsets(out, cls, m_name, version, a_type, m_type, offset):
1582 """
1583 Generate the sub-object offset and length calculations for accessors
1584 @param out File being written
1585 @param m_name Name of member
1586 @param version Wire version being processed
1587 @param a_type The accessor type (set or get)
1588 @param m_type The original member type
1589 @param offset The offset of the object or -1 if not fixed
1590 """
1591 # determine offset
1592 o_str = "%d" % offset # Default is fixed length
1593 if offset == -1:
1594 # There are currently 4 special cases for this
1595 # In general, get offset and length of predecessor
1596 if (loxi_utils.cls_is_flow_mod(cls) and m_name == "instructions"):
1597 pass
1598 elif (cls == "of_flow_stats_entry" and m_name == "instructions"):
1599 pass
1600 elif (cls == "of_packet_in" and m_name == "data"):
1601 pass
1602 elif (cls == "of_packet_out" and m_name == "data"):
1603 pass
Rich Lane713d9282013-12-30 15:21:35 -08001604 elif (cls == "of_bsn_gentable_entry_add" and m_name == "value"):
1605 pass
1606 elif (cls == "of_bsn_gentable_entry_desc_stats_entry" and m_name == "value"):
1607 pass
1608 elif (cls == "of_bsn_gentable_entry_stats_entry" and m_name == "stats"):
1609 pass
Rich Lanea06d0c32013-03-25 08:52:03 -07001610 else:
1611 debug("Error: Unknown member with offset == -1")
1612 debug(" cls %s, m_name %s, version %d" % (cls, m_name, version))
1613 sys.exit(1)
1614 o_str = "_%s_%s_OFFSET(obj)" % (cls.upper()[3:], m_name.upper())
1615
1616 out.write("""\
1617 offset = %s;
1618""" % o_str);
1619
1620 # This could be moved to main body but for version check
1621 if not loxi_utils.type_is_scalar(m_type):
1622 if loxi_utils.class_is_var_len(m_type[:-2], version) or \
1623 m_type == "of_match_t":
Rich Lane713d9282013-12-30 15:21:35 -08001624 len_macro = get_len_macro(cls, m_name, m_type, version)
Rich Lanea06d0c32013-03-25 08:52:03 -07001625 else:
1626 len_macro = "%d" % of_g.base_length[(m_type[:-2], version)]
1627 out.write(" cur_len = %s;\n" % len_macro)
1628 out.write(" break;\n")
1629
1630def length_of(m_type, version):
1631 """
1632 Return the length of a type based on the version
1633 """
1634 if m_type in of_g.of_mixed_types:
1635 m_type = of_g.of_mixed_types[m_type][version]
1636 if m_type in of_g.of_base_types:
1637 return of_g.of_base_types[m_type]["bytes"]
1638 if (m_type[:-2], version) in of_g.base_length:
1639 return of_g.base_length[(m_type[:-2], version)]
1640 print "Unknown length request", m_type, version
1641 sys.exit(1)
Andreas Wundsam53256162013-05-02 14:05:53 -07001642
Rich Lanea06d0c32013-03-25 08:52:03 -07001643
1644def gen_get_accessor_body(out, cls, m_type, m_name):
1645 """
1646 Generate the common operations for a get accessor
1647 """
1648 if loxi_utils.type_is_scalar(m_type):
1649 ver = "" # See if version required for scalar update
1650 if m_type in of_g.of_mixed_types:
1651 ver = "ver, "
1652 out.write("""\
1653 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s);
1654""" % dict(wa=wire_accessor(m_type, "get"), ver=ver, m_name=m_name))
1655
1656 if m_type == "of_port_no_t":
1657 out.write(" OF_PORT_NO_VALUE_CHECK(*%s, ver);\n" % m_name)
1658 elif m_type == "of_octets_t":
1659 out.write("""\
Rich Lanee57f0432014-02-19 10:31:53 -08001660 LOCI_ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
Rich Lanea06d0c32013-03-25 08:52:03 -07001661 %(m_name)s->bytes = cur_len;
1662 %(m_name)s->data = OF_WIRE_BUFFER_INDEX(wbuf, abs_offset);
1663""" % dict(m_name=m_name))
1664 elif m_type == "of_match_t":
1665 out.write("""
Rich Lanee57f0432014-02-19 10:31:53 -08001666 LOCI_ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
Rich Lanea06d0c32013-03-25 08:52:03 -07001667 match_octets.bytes = cur_len;
1668 match_octets.data = OF_OBJECT_BUFFER_INDEX(obj, offset);
1669 OF_TRY(of_match_deserialize(ver, %(m_name)s, &match_octets));
1670""" % dict(m_name=m_name))
1671 else:
1672 out.write("""
1673 /* Initialize child */
1674 %(m_type)s_init(%(m_name)s, obj->version, 0, 1);
1675 /* Attach to parent */
1676 %(m_name)s->parent = (of_object_t *)obj;
1677 %(m_name)s->wire_object.wbuf = obj->wire_object.wbuf;
1678 %(m_name)s->wire_object.obj_offset = abs_offset;
1679 %(m_name)s->wire_object.owned = 0;
1680 %(m_name)s->length = cur_len;
1681""" % dict(m_type=m_type[:-2], m_name=m_name))
1682
1683
1684def gen_set_accessor_body(out, cls, m_type, m_name):
1685 """
1686 Generate the contents of a set accessor
1687 """
1688 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1689 ver = "" # See if version required for scalar update
1690 if m_type in of_g.of_mixed_types:
1691 ver = "ver, "
1692 cur_len = "" # See if version required for scalar update
1693 if m_type == "of_octets_t":
1694 cur_len = ", cur_len"
1695 out.write("""\
1696 new_len = %(m_name)s->bytes;
1697 of_wire_buffer_grow(wbuf, abs_offset + (new_len - cur_len));
1698""" % dict(m_name=m_name))
1699 out.write("""\
1700 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s%(cur_len)s);
1701""" % dict(wa=wire_accessor(m_type, "set"), ver=ver, cur_len=cur_len,
1702 m_name=m_name))
1703
1704 elif m_type == "of_match_t":
1705 out.write("""
1706 /* Match object */
1707 OF_TRY(of_match_serialize(ver, %(m_name)s, &match_octets));
1708 new_len = match_octets.bytes;
1709 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1710 match_octets.data, new_len);
1711 /* Free match serialized octets */
1712 FREE(match_octets.data);
1713""" % dict(m_name=m_name))
1714
1715 else: # Other object type
1716 out.write("\n /* LOCI object type */")
1717 # Need to special case OXM list
1718 out.write("""
1719 new_len = %(m_name)s->length;
1720 /* If underlying buffer already shared; nothing to do */
1721 if (obj->wire_object.wbuf == %(m_name)s->wire_object.wbuf) {
1722 of_wire_buffer_grow(wbuf, abs_offset + new_len);
1723 /* Verify that the offsets are correct */
Rich Lanee57f0432014-02-19 10:31:53 -08001724 LOCI_ASSERT(abs_offset == OF_OBJECT_ABSOLUTE_OFFSET(%(m_name)s, 0));
1725 /* LOCI_ASSERT(new_len == cur_len); */ /* fixme: may fail for OXM lists */
Rich Lanea06d0c32013-03-25 08:52:03 -07001726 return %(ret_success)s;
1727 }
1728
1729 /* Otherwise, replace existing object in data buffer */
1730 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1731 OF_OBJECT_BUFFER_INDEX(%(m_name)s, 0), new_len);
1732""" % dict(m_name=m_name, ret_success=accessor_return_success("set", m_type)))
1733
1734 if not loxi_utils.type_is_scalar(m_type):
1735 if cls == "of_packet_out" and m_type == "of_list_action_t":
1736 out.write("""
1737 /* Special case for setting action lengths */
1738 _PACKET_OUT_ACTION_LEN_SET(obj, %(m_name)s->length);
1739""" % dict(m_name=m_name))
Rich Lane713d9282013-12-30 15:21:35 -08001740 elif cls == "of_bsn_gentable_entry_add" and m_name == "key":
1741 out.write("""
1742 /* Special case for setting key length */
1743 of_object_u16_set(obj, 18, %(m_name)s->length);
1744""" % dict(m_name=m_name))
1745 elif cls in ["of_bsn_gentable_entry_desc_stats_entry", "of_bsn_gentable_entry_stats_entry"] and m_name == "key":
1746 out.write("""
1747 /* Special case for setting key length */
1748 of_object_u16_set(obj, 2, %(m_name)s->length);
1749""" % dict(m_name=m_name))
Rich Lanea06d0c32013-03-25 08:52:03 -07001750 elif m_type not in ["of_match_t", "of_octets_t"]:
1751 out.write("""
1752 /* @fixme Shouldn't this precede copying value's data to buffer? */
1753 if (%(m_name)s->wire_length_set != NULL) {
1754 %(m_name)s->wire_length_set((of_object_t *)%(m_name)s, %(m_name)s->length);
1755 }
1756""" % dict(m_name=m_name))
1757 out.write("""
1758 /* Not scalar, update lengths if needed */
1759 delta = new_len - cur_len;
1760 if (delta != 0) {
1761 /* Update parent(s) */
1762 of_object_parent_length_update((of_object_t *)obj, delta);
1763 }
1764""")
1765
1766def obj_assert_check(cls):
1767 """
1768 The body of the assert check for an accessor
1769 We allow all versions of add/delete/modify to use the same accessors
1770 """
1771 if cls in ["of_flow_modify", "of_flow_modify_strict",
1772 "of_flow_delete", "of_flow_delete_strict",
1773 "of_flow_add"]:
1774 return "IS_FLOW_MOD_SUBTYPE(obj->object_id)"
1775 else:
1776 return "obj->object_id == %s" % cls.upper()
1777
1778def gen_of_object_get(out, cls, m_name, m_type):
1779 sub_cls = m_type[:-2]
1780 out.write("""
1781/**
Andreas Wundsam53256162013-05-02 14:05:53 -07001782 * Create a copy of %(m_name)s into a new variable of type %(m_type)s from
Rich Lanea06d0c32013-03-25 08:52:03 -07001783 * a %(cls)s instance.
1784 *
1785 * @param obj Pointer to the source of type %(cls)s_t
1786 * @returns A pointer to a new instance of type %(m_type)s whose contents
1787 * match that of %(m_name)s from source
1788 * @returns NULL if an error occurs
1789 */
1790%(m_type)s *
1791%(cls)s_%(m_name)s_get(%(cls)s_t *obj) {
1792 %(m_type)s _%(m_name)s;
1793 %(m_type)s *_%(m_name)s_ptr;
1794
1795 %(cls)s_%(m_name)s_bind(obj, &_%(m_name)s);
1796 _%(m_name)s_ptr = (%(m_type)s *)of_object_dup(&_%(m_name)s);
1797 return _%(m_name)s_ptr;
1798}
1799""" % dict(m_name=m_name, m_type=m_type, cls=cls, sub_cls=sub_cls))
1800
1801def gen_unified_acc_body(out, cls, m_name, ver_type_map, a_type, m_type):
1802 """
1803 Generate the body of a set or get accessor function
1804
1805 @param out The file to which to write the decs
1806 @param cls The class name
1807 @param m_name The member name
1808 @param ver_type_map Maps (type, offset) pairs to a list of versions
1809 @param a_type The accessor type, set or get
1810 @param m_type The original member type
1811
1812 The type values in ver_type_map are now ignored as we've pushed down
1813 the type munging to the lower level.
1814
1815 This is unified because the version switch case processing is the
1816 same for both set and get
1817 """
1818 out.write("""{
1819 of_wire_buffer_t *wbuf;
1820 int offset = 0; /* Offset of value relative to the start obj */
1821 int abs_offset; /* Offset of value relative to start of wbuf */
1822 of_version_t ver;
1823""")
1824
1825 if not loxi_utils.type_is_scalar(m_type):
1826 out.write("""\
1827 int cur_len = 0; /* Current length of object data */
1828""")
1829 if a_type == "set":
1830 out.write("""\
1831 int new_len, delta; /* For set, need new length and delta */
1832""")
1833
1834 # For match, need octet string for set/get
1835 if m_type == "of_match_t":
1836 out.write("""\
1837 of_octets_t match_octets; /* Serialized string for match */
1838""")
1839
1840 out.write("""
Rich Lanee57f0432014-02-19 10:31:53 -08001841 LOCI_ASSERT(%(assert_str)s);
Rich Lanea06d0c32013-03-25 08:52:03 -07001842 ver = obj->version;
1843 wbuf = OF_OBJECT_TO_WBUF(obj);
Rich Lanee57f0432014-02-19 10:31:53 -08001844 LOCI_ASSERT(wbuf != NULL);
Rich Lanea06d0c32013-03-25 08:52:03 -07001845
1846 /* By version, determine offset and current length (where needed) */
1847 switch (ver) {
1848""" % dict(assert_str=obj_assert_check(cls)))
1849
1850 for first in sorted(ver_type_map):
1851 (prev_t, prev_o) = ver_type_map[first]
1852 prev_len = length_of(prev_t, first)
1853 prev = first
1854 out.write(" case %s:\n" % of_g.wire_ver_map[first])
1855 break
1856
1857 for next in sorted(ver_type_map):
1858 if next == first:
1859 continue
1860
1861 (t, o) = ver_type_map[next]
1862 cur_len = length_of(t, next)
1863 if o == prev_o and cur_len == prev_len:
1864 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1865 continue
1866 gen_accessor_offsets(out, cls, m_name, prev, a_type, m_type, prev_o)
1867 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1868 (prev_t, prev_o, prev_len, prev) = (t, o, cur_len, next)
1869
1870 gen_accessor_offsets(out, cls, m_name, next, a_type, m_type, prev_o)
1871 out.write("""\
1872 default:
Rich Lanee57f0432014-02-19 10:31:53 -08001873 LOCI_ASSERT(0);
Rich Lanea06d0c32013-03-25 08:52:03 -07001874 }
1875
1876 abs_offset = OF_OBJECT_ABSOLUTE_OFFSET(obj, offset);
Rich Lanee57f0432014-02-19 10:31:53 -08001877 LOCI_ASSERT(abs_offset >= 0);
Rich Lanea06d0c32013-03-25 08:52:03 -07001878""")
1879 if not loxi_utils.type_is_scalar(m_type):
Rich Lanee57f0432014-02-19 10:31:53 -08001880 out.write(" LOCI_ASSERT(cur_len >= 0 && cur_len < 64 * 1024);\n")
Rich Lanea06d0c32013-03-25 08:52:03 -07001881
1882 # Now generate the common accessor code
1883 if a_type in ["get", "bind"]:
1884 gen_get_accessor_body(out, cls, m_type, m_name)
1885 else:
1886 gen_set_accessor_body(out, cls, m_type, m_name)
1887
1888 out.write("""
1889 OF_LENGTH_CHECK_ASSERT(obj);
1890
1891 return %s;
1892}
1893""" % accessor_return_success(a_type, m_type))
1894
1895def gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map):
1896 """
1897 For generating the bind call for OF sub-objects
1898 """
1899
1900 params = ",\n ".join(param_list(cls, m_name, "bind"))
1901 out.write("""
1902/**
1903 * Bind an object of type %(m_type)s to the parent of type %(cls)s for
1904 * member %(m_name)s
1905 * @param obj Pointer to an object of type %(cls)s.
1906 * @param %(m_name)s Pointer to the child object of type
1907 * %(m_type)s to be filled out.
1908 * \ingroup %(cls)s
1909 *
1910 * The parameter %(m_name)s is filled out to point to the same underlying
1911 * wire buffer as its parent.
1912 *
1913 */
1914""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1915
1916 ret_type = accessor_return_type("bind", m_type)
1917 out.write("%s\n%s_%s_bind(\n %s)\n" % (ret_type, cls, m_name, params))
1918 gen_unified_acc_body(out, cls, m_name, ver_type_map, "bind", m_type)
1919
1920def gen_get_accessor(out, cls, m_name, m_type, ver_type_map):
1921 """
1922 For generating the get call for non- OF sub-objects
1923 """
1924 params = ",\n ".join(param_list(cls, m_name, "get"))
1925 out.write("""
1926/**
1927 * Get %(m_name)s from an object of type %(cls)s.
1928 * @param obj Pointer to an object of type %(cls)s.
1929 * @param %(m_name)s Pointer to the child object of type
1930 * %(m_type)s to be filled out.
1931 *
1932 */
1933""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1934
1935 ret_type = accessor_return_type("get", m_type)
1936 out.write("%s\n%s_%s_get(\n %s)\n" % (ret_type, cls, m_name, params))
1937 gen_unified_acc_body(out, cls, m_name, ver_type_map, "get", m_type)
1938
Rich Lanece2e4642013-12-15 12:05:45 -08001939def gen_accessor_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07001940 for m_name in of_g.ordered_members[cls]:
1941 if m_name in of_g.skip_members:
1942 continue
1943 m_type = loxi_utils.member_base_type(cls, m_name)
1944 ver_type_map = field_ver_get(cls, m_name)
1945
1946 # Generate get/bind pending on member type
1947 # FIXME: Does this do the right thing for match?
1948 if loxi_utils.type_is_of_object(m_type):
1949 gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map)
1950 gen_of_object_get(out, cls, m_name, m_type)
1951 else:
1952 gen_get_accessor(out, cls, m_name, m_type, ver_type_map)
1953
1954 # Now generate set accessor for all objects
1955 params = ",\n ".join(param_list(cls, m_name, "set"))
1956 out.write("""
1957/**
1958 * Set %(m_name)s in an object of type %(cls)s.
1959 * @param obj Pointer to an object of type %(cls)s.
1960""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1961 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1962 out.write("""\
1963 * @param %(m_name)s The value to write into the object
1964 */
1965""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1966 else:
1967 out.write("""\
1968 * @param %(m_name)s Pointer to the child of type %(m_type)s.
1969 *
1970 * If the child's wire buffer is the same as the parent's, then
1971 * nothing is done as the changes have already been registered in the
1972 * parent. Otherwise, the data in the child's wire buffer is inserted
1973 * into the parent's and the appropriate lengths are updated.
1974 */
1975""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1976 ret_type = accessor_return_type("set", m_type)
1977 out.write("%s\n%s_%s_set(\n %s)\n" % (ret_type, cls, m_name, params))
1978 gen_unified_acc_body(out, cls, m_name, ver_type_map, "set", m_type)
1979
Rich Lanea06d0c32013-03-25 08:52:03 -07001980################################################################
1981#
1982# New/Delete Function Definitions
1983#
1984################################################################
1985
1986
1987################################################################
1988# First, some utility functions for new/delete
1989################################################################
1990
1991def del_function_proto(cls):
1992 """
1993 Return the prototype for the delete operator for the given class
1994 @param cls The class name
1995 """
1996 fn = "void\n"
1997 return fn
1998
1999
Rich Lanea06d0c32013-03-25 08:52:03 -07002000################################################################
2001# Routines to generate the body of new/delete functions
2002################################################################
2003
2004def gen_init_fn_body(cls, out):
2005 """
2006 Generate function body for init function
2007 @param cls The class name for the function
2008 @param out The file to which to write
2009 """
2010 if cls in type_maps.inheritance_map:
2011 param = "obj_p"
2012 else:
2013 param = "obj"
2014
2015 out.write("""
2016/**
2017 * Initialize an object of type %(cls)s.
2018 *
2019 * @param obj Pointer to the object to initialize
2020 * @param version The wire version to use for the object
2021 * @param bytes How many bytes in the object
2022 * @param clean_wire Boolean: If true, clear the wire object control struct
2023 *
2024 * If bytes < 0, then the default fixed length is used for the object
2025 *
2026 * This is a "coerce" function that sets up the pointers for the
Andreas Wundsam53256162013-05-02 14:05:53 -07002027 * accessors properly.
Rich Lanea06d0c32013-03-25 08:52:03 -07002028 *
2029 * If anything other than 0 is passed in for the buffer size, the underlying
2030 * wire buffer will have 'grow' called.
2031 */
2032
2033void
2034%(cls)s_init(%(cls)s_t *%(param)s,
2035 of_version_t version, int bytes, int clean_wire)
2036{
2037""" % dict(cls=cls, param=param))
2038
2039 # Use an extra pointer to deal with inheritance classes
2040 if cls in type_maps.inheritance_map:
2041 out.write("""\
2042 %s_header_t *obj;
2043
2044 obj = &obj_p->header; /* Need instantiable subclass */
2045""" % cls)
2046
2047 out.write("""
Rich Lanee57f0432014-02-19 10:31:53 -08002048 LOCI_ASSERT(of_object_fixed_len[version][%(enum)s] >= 0);
Rich Lanea06d0c32013-03-25 08:52:03 -07002049 if (clean_wire) {
2050 MEMSET(obj, 0, sizeof(*obj));
2051 }
2052 if (bytes < 0) {
Rich Lanef70be942013-07-18 13:33:14 -07002053 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002054 }
2055 obj->version = version;
2056 obj->length = bytes;
2057 obj->object_id = %(enum)s;
2058""" % dict(cls=cls, enum=enum_name(cls)))
2059 gen_coerce_ops(out, cls)
2060
2061 out.write("""
2062 /* Grow the wire buffer */
2063 if (obj->wire_object.wbuf != NULL) {
2064 int tot_bytes;
2065
2066 tot_bytes = bytes + obj->wire_object.obj_offset;
2067 of_wire_buffer_grow(obj->wire_object.wbuf, tot_bytes);
2068 }
2069}
2070
2071""")
2072
2073## @fixme This should also be updated once there is a map from
2074# class instance to wire length/type accessors
2075def gen_wire_push_fn(cls, out):
2076 """
2077 Generate the calls to push values into the wire buffer
2078 """
2079 if type_maps.class_is_virtual(cls):
2080 print "Push fn gen called for virtual class " + cls
2081 return
2082
2083 out.write("""
2084/**
2085 * Helper function to push values into the wire buffer
2086 */
2087static inline int
2088%(cls)s_push_wire_values(%(cls)s_t *obj)
2089{
2090""" % dict(cls=cls))
2091
Rich Lanebdd8e292013-12-06 17:37:39 -08002092 import loxi_globals
2093 uclass = loxi_globals.unified.class_by_name(cls)
2094 if uclass and not uclass.virtual and uclass.has_type_members:
2095 out.write("""
2096 %(cls)s_push_wire_types(obj);
2097""" % dict(cls=cls))
2098
Rich Lanea06d0c32013-03-25 08:52:03 -07002099 if loxi_utils.class_is_message(cls):
2100 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002101 /* Message obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002102 of_message_t msg;
2103
2104 if ((msg = OF_OBJECT_TO_MESSAGE(obj)) != NULL) {
Rich Lanea06d0c32013-03-25 08:52:03 -07002105 of_message_length_set(msg, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002106 }
2107""" % dict(name = enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002108
Rich Lanea06d0c32013-03-25 08:52:03 -07002109 else: # Not a message
2110 if loxi_utils.class_is_tlv16(cls):
2111 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002112 /* TLV obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002113 of_tlv16_wire_length_set((of_object_t *)obj, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002114""" % dict(enum=enum_name(cls)))
Rich Lanea06d0c32013-03-25 08:52:03 -07002115
Rich Lanea06d0c32013-03-25 08:52:03 -07002116 if loxi_utils.class_is_u16_len(cls) or cls == "of_packet_queue":
2117 out.write("""
2118 obj->wire_length_set((of_object_t *)obj, obj->length);
2119""")
2120
2121 if cls == "of_meter_stats":
2122 out.write("""
2123 of_meter_stats_wire_length_set((of_object_t *)obj, obj->length);
2124""" % dict(enum=enum_name(cls)))
2125
2126 out.write("""
2127 return OF_ERROR_NONE;
2128}
2129""")
2130
2131def gen_new_fn_body(cls, out):
2132 """
2133 Generate function body for new function
2134 @param cls The class name for the function
2135 @param out The file to which to write
2136 """
2137
2138 out.write("""
2139/**
2140 * \\defgroup %(cls)s %(cls)s
2141 */
2142""" % dict(cls=cls))
2143
2144 if not type_maps.class_is_virtual(cls):
2145 gen_wire_push_fn(cls, out)
2146
2147 out.write("""
2148/**
2149 * Create a new %(cls)s object
2150 *
2151 * @param version The wire version to use for the object
2152 * @return Pointer to the newly create object or NULL on error
2153 *
2154 * Initializes the new object with it's default fixed length associating
2155 * a new underlying wire buffer.
2156 *
2157 * Use new_from_message to bind an existing message to a message object,
2158 * or a _get function for non-message objects.
2159 *
2160 * \\ingroup %(cls)s
2161 */
2162
2163%(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002164%(cls)s_new(of_version_t version)
Rich Lanea06d0c32013-03-25 08:52:03 -07002165{
2166 %(cls)s_t *obj;
2167 int bytes;
2168
Rich Lanef70be942013-07-18 13:33:14 -07002169 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002170
2171 /* Allocate a maximum-length wire buffer assuming we'll be appending to it. */
2172 if ((obj = (%(cls)s_t *)of_object_new(OF_WIRE_BUFFER_MAX_LENGTH)) == NULL) {
2173 return NULL;
2174 }
2175
2176 %(cls)s_init(obj, version, bytes, 0);
2177""" % dict(cls=cls, enum=enum_name(cls)))
2178 if not type_maps.class_is_virtual(cls):
2179 out.write("""
2180 if (%(cls)s_push_wire_values(obj) < 0) {
2181 FREE(obj);
2182 return NULL;
2183 }
2184""" % dict(cls=cls))
2185
2186 match_offset = v3_match_offset_get(cls)
2187 if match_offset >= 0:
2188 # Init length field for match object
2189 out.write("""
2190 /* Initialize match TLV for 1.2 */
2191 /* FIXME: Check 1.3 below */
2192 if ((version == OF_VERSION_1_2) || (version == OF_VERSION_1_3)) {
2193 of_object_u16_set((of_object_t *)obj, %(match_offset)d + 2, 4);
2194 }
2195""" % dict(match_offset=match_offset))
2196 out.write("""
2197 return obj;
2198}
Rich Lanea06d0c32013-03-25 08:52:03 -07002199""" % dict(cls=cls))
2200
2201
2202def gen_from_message_fn_body(cls, out):
2203 """
2204 Generate function body for from_message function
2205 @param cls The class name for the function
2206 @param out The file to which to write
2207 """
2208 out.write("""
2209/**
2210 * Create a new %(cls)s object and bind it to an existing message
2211 *
2212 * @param msg The message to bind the new object to
2213 * @return Pointer to the newly create object or NULL on error
2214 *
2215 * \ingroup %(cls)s
2216 */
2217
2218%(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002219%(cls)s_new_from_message(of_message_t msg)
Rich Lanea06d0c32013-03-25 08:52:03 -07002220{
2221 %(cls)s_t *obj = NULL;
2222 of_version_t version;
2223 int length;
2224
2225 if (msg == NULL) return NULL;
2226
2227 version = of_message_version_get(msg);
2228 if (!OF_VERSION_OKAY(version)) return NULL;
2229
2230 length = of_message_length_get(msg);
2231
2232 if ((obj = (%(cls)s_t *)of_object_new(-1)) == NULL) {
2233 return NULL;
2234 }
2235
2236 %(cls)s_init(obj, version, 0, 0);
2237
2238 if ((of_object_buffer_bind((of_object_t *)obj, OF_MESSAGE_TO_BUFFER(msg),
2239 length, OF_MESSAGE_FREE_FUNCTION)) < 0) {
2240 FREE(obj);
2241 return NULL;
2242 }
2243 obj->length = length;
2244 obj->version = version;
2245
2246 return obj;
2247}
Rich Lanea06d0c32013-03-25 08:52:03 -07002248""" % dict(cls=cls))
2249
2250
2251################################################################
2252# Now the top level generator functions
2253################################################################
2254
2255def gen_new_function_declarations(out):
2256 """
2257 Gerenate the header file declarations for new operators for all classes
2258 @param out The file to which to write the decs
2259 """
2260
2261 out.write("""
2262/****************************************************************
2263 *
2264 * New operator declarations
2265 *
2266 * _new: Create a new object for writing; includes init
2267 * _new_from_message: Create a new instance of the object and bind the
2268 * message data to the object
2269 * _init: Initialize and optionally allocate buffer space for an
2270 * automatic instance
2271 *
2272 * _new and _from_message require a delete operation to be called
2273 * on the object.
2274 *
2275 ****************************************************************/
2276""")
Rich Lanea06d0c32013-03-25 08:52:03 -07002277
2278 for cls in of_g.standard_class_order:
2279 out.write("""
2280extern %(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002281 %(cls)s_new(of_version_t version);
Rich Lanea06d0c32013-03-25 08:52:03 -07002282""" % dict(cls=cls))
2283 if loxi_utils.class_is_message(cls):
2284 out.write("""extern %(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002285 %(cls)s_new_from_message(of_message_t msg);
Rich Lanea06d0c32013-03-25 08:52:03 -07002286""" % dict(cls=cls))
2287 out.write("""extern void %(cls)s_init(
2288 %(cls)s_t *obj, of_version_t version, int bytes, int clean_wire);
2289""" % dict(cls=cls))
2290
2291 out.write("""
2292/****************************************************************
2293 *
2294 * Delete operator static inline definitions.
2295 * These are here for type checking purposes only
2296 *
2297 ****************************************************************/
2298""")
2299 for cls in of_g.standard_class_order:
2300# if cls in type_maps.inheritance_map:
2301# continue
2302 out.write("""
2303/**
2304 * Delete an object of type %(cls)s_t
2305 * @param obj An instance of type %(cls)s_t
2306 *
2307 * \ingroup %(cls)s
2308 */
2309static inline void
2310%(cls)s_delete(%(cls)s_t *obj) {
2311 of_object_delete((of_object_t *)(obj));
2312}
2313""" % dict(cls=cls))
2314
2315 out.write("""
2316typedef void (*of_object_init_f)(of_object_t *obj, of_version_t version,
2317 int bytes, int clean_wire);
Rich Laneb157b0f2013-03-27 13:55:28 -07002318extern const of_object_init_f of_object_init_map[];
Rich Lanea06d0c32013-03-25 08:52:03 -07002319""")
2320
2321 out.write("""
2322/****************************************************************
2323 *
2324 * Function pointer initialization functions
2325 * These are part of the "coerce" type casting for objects
2326 *
2327 ****************************************************************/
2328""")
2329
2330#
2331# @fixme Not clear that these should all be set for virtual fns
2332#
2333# @fixme Clean up. should have a (language specific) map from class
2334# to length and type get/set functions
2335#
2336
2337def gen_coerce_ops(out, cls):
2338 out.write("""
2339 /* Set up the object's function pointers */
2340""")
2341
Rich Lane92feca82013-12-10 15:57:13 -08002342 uclass = loxi_globals.unified.class_by_name(cls)
2343 if uclass and not uclass.virtual and uclass.has_type_members:
2344 out.write("""
2345 obj->wire_type_set = %(cls)s_push_wire_types;
2346""" % dict(cls=cls))
2347
Rich Lanea06d0c32013-03-25 08:52:03 -07002348 if loxi_utils.class_is_message(cls):
2349 out.write("""
2350 obj->wire_length_get = of_object_message_wire_length_get;
2351 obj->wire_length_set = of_object_message_wire_length_set;
2352""")
2353 else:
2354 if loxi_utils.class_is_tlv16(cls):
2355 if not (cls in type_maps.inheritance_map): # Don't set for super
2356 out.write("""
2357 obj->wire_length_set = of_tlv16_wire_length_set;
Rich Lanea06d0c32013-03-25 08:52:03 -07002358""")
2359 out.write("""
2360 obj->wire_length_get = of_tlv16_wire_length_get;
2361""")
2362 if loxi_utils.class_is_action(cls):
2363 out.write("""
2364 obj->wire_type_get = of_action_wire_object_id_get;
2365""")
2366 if loxi_utils.class_is_action_id(cls):
2367 out.write("""
2368 obj->wire_type_get = of_action_id_wire_object_id_get;
2369""")
2370 if loxi_utils.class_is_instruction(cls):
2371 out.write("""
2372 obj->wire_type_get = of_instruction_wire_object_id_get;
2373""")
2374 if loxi_utils.class_is_queue_prop(cls):
2375 out.write("""
2376 obj->wire_type_get = of_queue_prop_wire_object_id_get;
2377""")
2378 if loxi_utils.class_is_table_feature_prop(cls):
2379 out.write("""
2380 obj->wire_type_get = of_table_feature_prop_wire_object_id_get;
2381""")
2382 if loxi_utils.class_is_meter_band(cls):
2383 out.write("""
2384 obj->wire_type_get = of_meter_band_wire_object_id_get;
2385""")
2386 if loxi_utils.class_is_hello_elem(cls):
2387 out.write("""
2388 obj->wire_type_get = of_hello_elem_wire_object_id_get;
2389""")
Rich Lane713d9282013-12-30 15:21:35 -08002390 if loxi_utils.class_is_bsn_tlv(cls):
2391 out.write("""
2392 obj->wire_type_get = of_bsn_tlv_wire_object_id_get;
2393""")
Rich Lanea06d0c32013-03-25 08:52:03 -07002394 if loxi_utils.class_is_oxm(cls):
2395 out.write("""
2396 obj->wire_length_get = of_oxm_wire_length_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002397 obj->wire_type_get = of_oxm_wire_object_id_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002398""")
2399 if loxi_utils.class_is_u16_len(cls):
2400 out.write("""
2401 obj->wire_length_get = of_u16_len_wire_length_get;
2402 obj->wire_length_set = of_u16_len_wire_length_set;
2403""")
2404 if cls == "of_packet_queue":
2405 out.write("""
2406 obj->wire_length_get = of_packet_queue_wire_length_get;
2407 obj->wire_length_set = of_packet_queue_wire_length_set;
2408""")
2409# if cls == "of_list_meter_band_stats":
2410# out.write("""
2411# obj->wire_length_get = of_list_meter_band_stats_wire_length_get;
2412#""")
2413 if cls == "of_meter_stats":
2414 out.write("""
2415 obj->wire_length_get = of_meter_stats_wire_length_get;
2416 obj->wire_length_set = of_meter_stats_wire_length_set;
2417""")
2418
Rich Laneb604e332013-12-15 13:23:51 -08002419def gen_new_function_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07002420 """
2421 Generate the new operator for all classes
2422
2423 @param out The file to which to write the functions
2424 """
2425
Rich Laneb604e332013-12-15 13:23:51 -08002426 gen_new_fn_body(cls, out)
2427 gen_init_fn_body(cls, out)
2428 if loxi_utils.class_is_message(cls):
2429 gen_from_message_fn_body(cls, out)
Rich Lanea06d0c32013-03-25 08:52:03 -07002430
Rich Lanea06d0c32013-03-25 08:52:03 -07002431"""
2432Document generation functions
2433
2434The main reason this is here is to generate documentation per
2435accessor that indicates the versions that support the interface.
2436"""
2437
2438
2439def gen_accessor_doc(out, name):
2440 """
2441 Generate documentation for each accessor function that indicates
2442 the versions supporting the accessor.
2443 """
2444
2445 common_top_matter(out, name)
2446
2447 out.write("/* DOCUMENTATION ONLY */\n")
2448
2449 for cls in of_g.standard_class_order:
2450 if cls in type_maps.inheritance_map:
2451 pass # Check this
2452
2453 out.write("""
2454/**
Andreas Wundsam53256162013-05-02 14:05:53 -07002455 * Structure for %(cls)s object. Get/set
Rich Lanea06d0c32013-03-25 08:52:03 -07002456 * accessors available in all versions unless noted otherwise
2457 *
2458""" % dict(cls=cls))
2459 if loxi_utils.class_is_list(cls):
2460 out.write("""\
2461 * @param first Function of type %(cls)s_first_f.
2462 * Setup a TBD class object to the first entry in the list
2463 * @param next Function of type %(cls)s_next_f.
2464 * Advance a TBD class object to the next entry in the list
2465 * @param append_bind Function of type %(cls)s_append_bind_f
2466 * Setup a TBD class object for append to the end of the current list
2467 * @param append Function of type @ref %(cls)s_append_f.
2468 * Copy an item to the end of a list
2469""" % dict(cls=cls))
2470
2471 for m_name in of_g.ordered_members[cls]:
2472 if m_name in of_g.skip_members:
2473 continue
2474 ver_type_map = field_ver_get(cls, m_name)
2475 (m_type, get_rv) = get_acc_rv(cls, m_name)
2476 if len(ver_type_map) == 3:
2477 # ver_string = "Available in all versions"
2478 ver_string = ""
2479 else:
2480 ver_string = "("
2481 for ver in sorted(ver_type_map):
2482 ver_string += " " + of_g.short_version_names[ver]
2483 ver_string += ")."
2484
2485 f_name = acc_name(cls, m_name)
2486 out.write("""\
2487 * @param %(m_name)s_get/set %(ver_string)s
2488 * Accessors for %(m_name)s, a variable of type %(m_type)s. Functions
2489 * are of type %(f_name)s_get_f and _set_f.
2490 *
2491""" % dict(f_name=f_name, m_name=m_name, ver_string=ver_string, m_type=m_type))
2492
2493 out.write("""\
2494 */
2495typedef struct %(cls)s_s %(cls)s_t;
2496""" % dict(cls=cls))
2497
2498 out.write("#endif /* _LOCI_DOC_H_ */\n")
2499
2500################################################################
2501#
2502# For fun, here are some unified, traditional C structure representation
2503#
2504################################################################
2505
2506def gen_cof_to_wire(out):
2507 pass
2508
2509def gen_wire_to_cof(out):
2510 pass
2511
2512def gen_cof_instance(out, cls):
2513 out.write("struct c%s_s {\n" % cls)
2514 for m in of_g.ordered_members[cls]:
2515 if m in of_g.skip_members:
2516 continue
2517 entry = of_g.unified[cls]["union"][m]
2518 cof_type = type_to_cof_type(entry["m_type"])
2519 out.write(" %-20s %s;\n" % (cof_type, m))
2520 out.write("};\n\n");
2521
2522def gen_cof_structs(out):
2523 """
2524 Generate non-version specific (common) representation of structures
2525
2526 @param out The file to which to write the functions
2527 """
2528
2529 out.write("\n/* Common, unified OpenFlow structure representations */\n")
2530 for cls in of_g.standard_class_order:
2531 if cls in type_maps.inheritance_map:
2532 continue
2533 gen_cof_instance(out, cls)
2534
2535################################################################
2536#
2537# Generate code samples for applications.
2538#
2539################################################################
2540
2541def gen_code_samples(out, name):
2542 out.write("""
2543#if 0 /* Do not compile in */
2544/**
2545 * @file %(name)s
2546 *
2547 * These are code samples for inclusion in other components
2548 */
2549
2550""" % dict(name=name))
2551
2552 gen_jump_table_template(out)
2553 # These are messages that a switch might expect.
2554 msg_list = ["of_echo_request",
2555 "of_hello",
2556 "of_packet_in",
2557 "of_packet_out",
2558 "of_port_mod",
2559 "of_port_stats_request",
2560 "of_queue_get_config_request",
2561 "of_queue_stats_request",
2562 "of_flow_add",
2563 "of_flow_modify",
2564 "of_flow_modify_strict",
2565 "of_flow_delete",
2566 "of_flow_delete_strict",
2567 "of_get_config_request",
2568 "of_flow_stats_request",
2569 "of_barrier_request",
2570 "of_echo_reply",
2571 "of_aggregate_stats_request",
2572 "of_desc_stats_request",
2573 "of_table_stats_request",
2574 "of_features_request",
2575 "of_table_mod",
2576 "of_set_config",
2577 "of_experimenter",
2578 "of_experimenter_stats_request",
2579 "of_group_desc_stats_request",
2580 "of_group_features_stats_request",
2581 "of_role_request"]
2582
2583 gen_message_handler_templates(out, msgs=msg_list)
2584
2585 out.write("""
2586#endif
2587""")
2588
2589def gen_jump_table_template(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002590 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002591 unhandled="unhandled_message"):
2592 """
2593 Generate a template for a jump table.
2594 @param out The file to which to write the functions
2595 """
2596 out.write("""
2597/*
2598 * Simple jump table definition for message handling
2599 */
2600typedef int (*msg_handler_f)(%(cxn_type)s cxn, of_object_t *obj);
2601typedef msg_handler_f msg_jump_table_t[OF_MESSAGE_OBJECT_COUNT];
2602
2603/* Jump table template for message objects */
2604extern msg_jump_table_t jump_table;
2605
2606/* C-code template */
2607msg_jump_table_t jump_table = {
2608 %(unhandled)s, /* OF_OBJECT; place holder for generic object */
2609""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2610 count = 0
2611 fn_name = unhandled
2612 for cls in of_g.ordered_messages:
2613 comma = ","
2614 count += 1
2615 if count == len(of_g.ordered_messages):
2616 comma = " "
2617 if not all_unhandled:
2618 fn_name = "%s_handler" % cls[3:]
2619 out.write(" %s%s /* %s */\n" % (fn_name, comma, enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002620
Rich Lanea06d0c32013-03-25 08:52:03 -07002621 out.write("};\n")
2622
2623def gen_message_switch_stmt_tmeplate(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002624 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002625 unhandled="unhandled_message"):
2626 out.write("""
2627/*
2628 * Simple switch statement for message handling
2629 */
2630
2631 switch (obj->object_id):
2632""")
2633 fn_name = unhandled
2634 for cls in of_g.ordered_messages:
2635 if not all_unhandled:
2636 fn_name = "%s_handler" % cls[3:]
2637 out.write("""
2638 case %(enum)s:
2639 rv = %(fn_name)s(cxn, obj);
2640 break;
2641""" % dict(fn_name=fn_name, cls=cls, enum=enum_name(cls)))
2642 out.write("""
2643 default:
2644 rv = LS_ERROR_PARAM;
2645 break;
2646 }
2647
2648 TRACE("Handled msg %p with rv %d (%s)", obj, rv, ls_error_strings[rv]);
2649
2650 return rv;
2651""")
2652
2653
2654def gen_message_handler_templates(out=sys.stdout, cxn_type="ls_cxn_handle_t",
2655 unhandled="unhandled_message", msgs=None):
2656 gen_jump_table_template(out, False, cxn_type)
2657 out.write("""
2658/**
2659 * Function for unhandled message
2660 */
2661static int
2662unhandled_message(%(cxn_type)s cxn, of_object_t *obj)
2663{
2664 (void)cxn;
2665 (void)obj;
2666 TRACE("Unhandled message %%p. Object id %%d", obj, obj->object_id);
2667
2668 return LS_ERROR_UNAVAIL;
2669}
2670""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2671
2672 if not msgs:
2673 msgs = of_g.ordered_messages
2674 for cls in msgs:
2675 out.write("""
2676/**
2677 * Handle a %(s_cls)s message
2678 * @param cxn Connection handler for the owning connection
2679 * @param _obj Generic type object for the message to be coerced
2680 * @returns Error code
2681 */
2682
2683static int
2684%(s_cls)s_handler(%(cxn_type)s cxn, of_object_t *_obj)
2685{
2686 %(cls)s_t *obj;
2687
2688 TRACE("Handling %(cls)s message: %%p.", obj);
2689 obj = (%(cls)s_t *)_obj;
2690
2691 /* Handle object of type %(cls)s_t */
2692
2693 return LS_ERROR_NONE;
2694}
2695""" % dict(s_cls=cls[3:], cls=cls, cxn_type=cxn_type))
2696 gen_message_switch_stmt_tmeplate(out, False, cxn_type)