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