blob: 81f5a1ac7dfe8b83e171c9aa64187e604b44ecb0 [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];
Rich Lanef8a3d002014-03-19 13:33:52 -0700703typedef char of_str64_t[64];
Rich Lanea06d0c32013-03-25 08:52:03 -0700704
Rich Lane3b2fd832013-09-24 13:44:08 -0700705typedef struct of_bitmap_128_s {
706 uint64_t hi;
707 uint64_t lo;
708} of_bitmap_128_t;
709
Rich Lanefab0c822013-12-30 11:46:48 -0800710typedef struct of_checksum_128_s {
711 uint64_t hi;
712 uint64_t lo;
713} of_checksum_128_t;
714
Rich Lanea06d0c32013-03-25 08:52:03 -0700715/* These are types which change across versions. */
716typedef uint32_t of_port_no_t;
717typedef uint16_t of_fm_cmd_t;
718typedef uint64_t of_wc_bmap_t;
719typedef uint64_t of_match_bmap_t;
720
721#define MEMMOVE(dest, src, bytes) memmove(dest, src, bytes)
722#define MEMSET(dest, val, bytes) memset(dest, val, bytes)
723#define MEMCPY(dest, src, bytes) memcpy(dest, src, bytes)
724#define MEMCMP(a, b, bytes) memcmp(a, b, bytes)
725#define MALLOC(bytes) malloc(bytes)
726#define FREE(ptr) free(ptr)
727
728/** Try an operation and return on failure. */
729#define OF_TRY(op) do { \\
730 int _rv; \\
731 if ((_rv = (op)) < 0) { \\
732 LOCI_LOG_ERROR("ERROR %d at %s:%d\\n", _rv, __FILE__, __LINE__); \\
733 return _rv; \\
734 } \\
735 } while (0)
736
737/* The extent of an OF match object is determined by its length field, but
738 * aligned to 8 bytes
739 */
740
741#define OF_MATCH_BYTES(length) (((length) + 7) & 0xfff8)
742
Rich Lanedfcafae2014-03-06 16:56:49 -0800743#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
Rich Lanea06d0c32013-03-25 08:52:03 -0700744#define U16_NTOH(val) (val)
745#define U32_NTOH(val) (val)
746#define U64_NTOH(val) (val)
747#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
748#define U16_HTON(val) (val)
749#define U32_HTON(val) (val)
750#define U64_HTON(val) (val)
751#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
752#else /* Little Endian */
Rich Lane69e247e2014-03-05 10:27:22 -0800753#define U16_NTOH(val) (((val) >> 8) | (((val) & 0xff) << 8))
Rich Lanea06d0c32013-03-25 08:52:03 -0700754#define U32_NTOH(val) ((((val) & 0xff000000) >> 24) | \\
755 (((val) & 0x00ff0000) >> 8) | \\
756 (((val) & 0x0000ff00) << 8) | \\
757 (((val) & 0x000000ff) << 24))
758#define U64_NTOH(val) ((((val) & 0xff00000000000000LL) >> 56) | \\
759 (((val) & 0x00ff000000000000LL) >> 40) | \\
760 (((val) & 0x0000ff0000000000LL) >> 24) | \\
761 (((val) & 0x000000ff00000000LL) >> 8) | \\
762 (((val) & 0x00000000ff000000LL) << 8) | \\
763 (((val) & 0x0000000000ff0000LL) << 24) | \\
764 (((val) & 0x000000000000ff00LL) << 40) | \\
765 (((val) & 0x00000000000000ffLL) << 56))
766#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
767#define U16_HTON(val) U16_NTOH(val)
768#define U32_HTON(val) U32_NTOH(val)
769#define U64_HTON(val) U64_NTOH(val)
770#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
771#endif
772
773/****************************************************************
774 *
775 * The following are internal definitions used by the automatically
776 * generated code. Users should not reference these definitions
777 * as they may change between versions of this code
778 *
779 ****************************************************************/
780
781#define OF_MESSAGE_IN_MATCH_POINTER(obj) \\
782 (WIRE_BUF_POINTER(&((obj)->wire_buffer), OF_MESSAGE_IN_MATCH_OFFSET))
783#define OF_MESSAGE_IN_MATCH_LEN(ptr) BUF_U16_GET(&ptr[2])
784#define OF_MESSAGE_IN_DATA_OFFSET(obj) \\
785 (FIXED_LEN + OF_MESSAGE_IN_MATCH_LEN(OF_MESSAGE_IN_MATCH_POINTER(obj)) + 2)
786
787#define OF_MESSAGE_OUT_DATA_OFFSET(obj) \\
788 (FIXED_LEN + of_message_out_actions_len_get(obj))
789
790""")
791
792def external_h_top_matter(out, name):
793 """
794 Generate top matter for external header file
795
796 @param name The name of the output file
797 @param out The output file object
798 """
799 common_top_matter(out, name)
800 out.write("""
801#include <loci/loci_base.h>
802#include <loci/of_message.h>
803#include <loci/of_match.h>
804#include <loci/of_object.h>
Rich Lanedef2e512013-12-15 15:54:02 -0800805#include <loci/loci_classes.h>
Rich Lanea06d0c32013-03-25 08:52:03 -0700806
807/****************************************************************
808 *
809 * This file is divided into the following sections.
810 *
811 * A few object specific macros
812 * Class typedefs (no struct definitions)
813 * Per-data type accessor function typedefs
814 * Per-class new/delete function typedefs
815 * Per-class static delete functions
816 * Per-class, per-member accessor declarations
817 * Per-class structure definitions
818 * Generic union (inheritance) definitions
819 * Pointer set function declarations
820 * Some special case macros
821 *
822 ****************************************************************/
823""")
824
Rich Lanea06d0c32013-03-25 08:52:03 -0700825################################################################
826#
827################################################################
828
829def gen_version_enum(out):
830 """
831 Generate the enumerated type for versions in LoxiGen
832 @param out The file object to which to write the decs
833
834 This just uses the wire versions for now
835 """
836 out.write("""
837/**
838 * Enumeration of OpenFlow versions
839 *
840 * The wire protocol numbers are currently used for values of the corresponding
841 * version identifiers.
842 */
843typedef enum of_version_e {
844 OF_VERSION_UNKNOWN = 0,
845""")
846
847 is_first = True
848 max = 0
849 for v in of_g.wire_ver_map:
850 if is_first:
851 is_first = False
852 else:
853 out.write(",\n")
854 if v > max:
855 max = v
856 out.write(" %s = %d" % (of_g.wire_ver_map[v], v))
857
858 out.write("""
859} of_version_t;
860
861/**
862 * @brief Use this when declaring arrays indexed by wire version
863 */
864#define OF_VERSION_ARRAY_MAX %d
865""" % (max + 1))
Andreas Wundsam53256162013-05-02 14:05:53 -0700866
Rich Lanea06d0c32013-03-25 08:52:03 -0700867def gen_object_enum(out):
868 """
869 Generate the enumerated type for object identification in LoxiGen
870 @param out The file object to which to write the decs
871 """
872 out.write("""
873
874/**
875 * Enumeration of OpenFlow objects
876 *
877 * We enumerate the OpenFlow objects used internally. Note that some
878 * message types are determined both by an outer type (message type like
879 * stats_request) and an inner type (port stats). These are different
880 * messages in ofC.
881 *
882 * These values are for internal use only. They will change with
883 * different versions of ofC.
884 */
885
886typedef enum of_object_id_e {
887 /* Root object type */
888 OF_OBJECT_INVALID = -1, /* "invalid" return value for mappings */
889 OF_OBJECT = 0, /* Generic, untyped object */
890
891 /* OpenFlow message objects */
892""")
893 last = 0
894 msg_count = 0
895 for cls in of_g.ordered_messages:
896 out.write(" %s = %d,\n" % (enum_name(cls),
897 of_g.unified[cls]["object_id"]))
898 msg_count = of_g.unified[cls]["object_id"] + 1
899
900 out.write("\n /* Non-message objects */\n")
901 for cls in of_g.ordered_non_messages:
902 out.write(" %s = %d,\n" % (enum_name(cls),
903 of_g.unified[cls]["object_id"]))
904 last = of_g.unified[cls]["object_id"]
905 out.write("\n /* List objects */\n")
906 for cls in of_g.ordered_list_objects:
907 out.write(" %s = %d,\n" % (enum_name(cls),
908 of_g.unified[cls]["object_id"]))
909 last = of_g.unified[cls]["object_id"]
910
911 out.write("\n /* Generic stats request/reply types; pseudo objects */\n")
912 for cls in of_g.ordered_pseudo_objects:
913 out.write(" %s = %d,\n" % (enum_name(cls),
914 of_g.unified[cls]["object_id"]))
915 last = of_g.unified[cls]["object_id"]
916
917 out.write("""
918 OF_OBJECT_COUNT = %d
919} of_object_id_t;
920
Rich Laneb157b0f2013-03-27 13:55:28 -0700921extern const char *const of_object_id_str[];
Rich Lanea06d0c32013-03-25 08:52:03 -0700922
923#define OF_MESSAGE_OBJECT_COUNT %d
924""" % ((last + 1), msg_count))
925
926 # Generate object type range checking for inheritance classes
927
928 # @fixme These should be determined algorithmicly
929 out.write("""
930/*
931 * Macros to check if an object ID is within an inheritance class range
932 */
933""")
934 # Alphabetical order for 'last'
935 last_ids = dict(of_action="OF_ACTION_STRIP_VLAN",
936 of_oxm="OF_OXM_VLAN_VID_MASKED",
937 of_instruction="OF_INSTRUCTION_WRITE_METADATA",
938 of_queue_prop="OF_QUEUE_PROP_MIN_RATE",
939 of_table_feature_prop="OF_TABLE_FEATURE_PROP_WRITE_SETFIELD_MISS",
940 # @FIXME add meter_band ?
941 )
942 for cls, last in last_ids.items():
943 out.write("""
944#define %(enum)s_FIRST_ID (%(enum)s + 1)
945#define %(enum)s_LAST_ID %(last)s
946#define %(enum)s_VALID_ID(id) \\
947 ((id) >= %(enum)s_FIRST_ID && \\
948 (id) <= %(enum)s_LAST_ID)
949""" % dict(enum=enum_name(cls), last=last))
950 out.write("""
951/**
952 * Function to check a wire ID
953 * @param object_id The ID to check
954 * @param base_object_id The inheritance parent, if applicable
955 * @returns boolean: If base_object_id is an inheritance class, check if
956 * object_id is valid as a subclass. Otherwise return 1.
957 *
958 * Note: Could check that object_id == base_object_id in the
959 * second case.
960 */
961static inline int
962of_wire_id_valid(int object_id, int base_object_id) {
963 switch (base_object_id) {
964 case OF_ACTION:
965 return OF_ACTION_VALID_ID(object_id);
966 case OF_OXM:
967 return OF_OXM_VALID_ID(object_id);
968 case OF_QUEUE_PROP:
969 return OF_QUEUE_PROP_VALID_ID(object_id);
970 case OF_TABLE_FEATURE_PROP:
971 return OF_TABLE_FEATURE_PROP_VALID_ID(object_id);
972 case OF_INSTRUCTION:
973 return OF_INSTRUCTION_VALID_ID(object_id);
974 default:
975 break;
976 }
977 return 1;
978}
979""")
980
Rich Lanea06d0c32013-03-25 08:52:03 -0700981################################################################
982#
983# Internal Utility Functions
984#
985################################################################
986
987
988def acc_name(cls, m_name):
989 """
990 Generate the root name of an accessor function for typedef
991 @param cls The class name
992 @param m_name The member name
993 """
994 (m_type, get_rv) = get_acc_rv(cls, m_name)
995 return "%s_%s" % (cls, m_type)
996
997def get_acc_rv(cls, m_name):
998 """
999 Determine the data type and return type for a get accessor.
1000
1001 The return type may be "void" or it may be the accessor type
1002 depending on the system configuration and on the data type.
1003
1004 @param cls The class name
1005 @param m_name The member name
1006 @return A pair (m_type, rv) where m_type is the unified type of the
1007 member and rv is the get_accessor return type
1008 """
1009 member = of_g.unified[cls]["union"][m_name]
1010 m_type = member["m_type"]
1011 rv = "int"
Rich Lanea06d0c32013-03-25 08:52:03 -07001012 if m_type[-2:] == "_t":
1013 m_type = m_type[:-2]
1014
1015 return (m_type, rv)
1016
1017def param_list(cls, m_name, a_type):
1018 """
1019 Generate the parameter list (no parens) for an a_type accessor
1020 @param cls The class name
1021 @param m_name The member name
1022 @param a_type One of "set" or "get" or TBD
1023 """
1024 member = of_g.unified[cls]["union"][m_name]
1025 m_type = member["m_type"]
1026 params = ["%s_t *obj" % cls]
1027 if a_type == "set":
1028 if loxi_utils.type_is_scalar(m_type):
1029 params.append("%s %s" % (m_type, m_name))
1030 else:
1031 params.append("%s *%s" % (m_type, m_name))
1032 elif a_type in ["get", "bind"]:
1033 params.append("%s *%s" % (m_type, m_name))
1034 else:
1035 debug("Class %s, name %s Bad param list a_type: %s" %
1036 (cls, m_name, a_type))
1037 sys.exit(1)
1038 return params
1039
1040def typed_function_base(cls, m_name):
1041 """
1042 Generate the core name for accessors based on the type
1043 @param cls The class name
1044 @param m_name The member name
1045 """
1046 (m_type, get_rv) = get_acc_rv(cls, m_name)
1047 return "%s_%s" % (cls, m_type)
1048
1049def member_function_base(cls, m_name):
1050 """
1051 Generate the core name for accessors based on the member name
1052 @param cls The class name
1053 @param m_name The member name
1054 """
1055 return "%s_%s" % (cls, m_name)
1056
1057def field_ver_get(cls, m_name):
1058 """
1059 Generate a dict, indexed by wire version, giving a pair (type, offset)
1060
1061 @param cls The class name
1062 @param m_name The name of the class member
1063
1064 If offset is not known for m_name, the type
1065 A dict is used for more convenient indexing.
1066 """
1067 result = {}
1068
1069 for ver in of_g.unified[cls]:
1070 if type(ver) == type(0): # It's a version
1071 if "use_version" in of_g.unified[cls][ver]: # deref version ref
1072 ref_ver = of_g.unified[cls][ver]["use_version"]
1073 members = of_g.unified[cls][ref_ver]["members"]
1074 else:
1075 members = of_g.unified[cls][ver]["members"]
1076 idx = loxi_utils.member_to_index(m_name, members)
1077 if (idx < 0):
1078 continue # Member not in this version
1079 m_type = members[idx]["m_type"]
1080 offset = members[idx]["offset"]
1081
1082 # If m_type is mixed, get wire version type from global data
1083 if m_type in of_g.of_mixed_types and \
1084 ver in of_g.of_mixed_types[m_type]:
1085 m_type = of_g.of_mixed_types[m_type][ver]
1086
1087 # add version to result list
1088 result[ver] = (m_type, offset)
1089
1090 return result
1091
1092def v3_match_offset_get(cls):
1093 """
Andreas Wundsam53256162013-05-02 14:05:53 -07001094 Return the offset of an OF 1.2 match in an object if it has such;
Rich Lanea06d0c32013-03-25 08:52:03 -07001095 otherwise return -1
1096 """
1097 result = field_ver_get(cls, "match")
1098 if of_g.VERSION_1_2 in result:
1099 if result[of_g.VERSION_1_2][0] == "of_match_v3_t":
1100 return result[of_g.VERSION_1_2][1]
1101 return -1
1102
1103################################################################
1104#
1105# OpenFlow Object Definitions
1106#
1107################################################################
1108
1109
1110def gen_of_object_defs(out):
1111 """
1112 Generate low level of_object core operations
1113 @param out The file for output, already open
1114 """
1115
1116def gen_generics(out):
1117 for (cls, subclasses) in type_maps.inheritance_map.items():
1118 out.write("""
1119/**
1120 * Inheritance super class for %(cls)s
1121 *
1122 * This class is the union of %(cls)s classes. You can refer
1123 * to it untyped by refering to the member 'header' whose structure
1124 * is common across all sub-classes.
1125 */
1126
1127union %(cls)s_u {
1128 %(cls)s_header_t header; /* Generic instance */
1129""" % dict(cls=cls))
1130 for subcls in sorted(subclasses):
1131 out.write(" %s_%s_t %s;\n" % (cls, subcls, subcls))
1132 out.write("};\n")
1133
1134def gen_struct_typedefs(out):
1135 """
1136 Generate typedefs for all struct objects
1137 @param out The file for output, already open
1138 """
1139
1140 out.write("\n/* LOCI inheritance parent typedefs */\n")
1141 for cls in type_maps.inheritance_map:
1142 out.write("typedef union %(cls)s_u %(cls)s_t;\n" % dict(cls=cls))
1143 out.write("\n/* LOCI object typedefs */\n")
1144 for cls in of_g.standard_class_order:
1145 if cls in type_maps.inheritance_map:
1146 continue
Rich Lane85767872013-12-15 16:24:42 -08001147 template = "typedef of_object_t %(cls)s_t;\n"
1148 out.write(template % dict(cls=cls))
Rich Lanea06d0c32013-03-25 08:52:03 -07001149
1150 out.write("""
1151/****************************************************************
1152 *
1153 * Additional of_object defines
1154 * These are needed for some static inline ops, so we put them here.
1155 *
1156 ****************************************************************/
1157
1158/* Delete an OpenFlow object without reference to its type */
1159extern void of_object_delete(of_object_t *obj);
1160
1161""")
1162
Rich Lanea06d0c32013-03-25 08:52:03 -07001163def gen_flow_add_setup_function_declarations(out):
1164 """
1165 Add the declarations for functions that can be initialized
1166 by a flow add. These are defined external to LOXI.
1167 """
1168
1169 out.write("""
1170/****************************************************************
1171 * Functions for objects that can be initialized by a flow add message.
1172 * These are defined in a non-autogenerated file
1173 ****************************************************************/
1174
1175/**
1176 * @brief Set up a flow removed message from the original add
1177 * @param obj The flow removed message being updated
1178 * @param flow_add The flow_add message to use
1179 *
1180 * Initialize the following fields of obj to be identical
1181 * to what was originally on the wire from the flow_add object:
1182 * match
1183 * cookie
1184 * priority
1185 * idle_timeout
1186 * hard_timeout
1187 *
1188 */
1189
1190extern int
1191of_flow_removed_setup_from_flow_add(of_flow_removed_t *obj,
1192 of_flow_add_t *flow_add);
1193
1194
1195/**
1196 * @brief Set up the packet in match structure from the original add
1197 * @param obj The packet in message being updated
1198 * @param flow_add The flow_add message to use
1199 * @returns Indigo error code. Does not return a version error if
1200 * the version does not require initializing obj.
1201 *
1202 * Initialize the match member of obj to be identical to what was originally
1203 * on the wire from the flow_add object. If applicable, the table ID is also
1204 * initialized from the flow_add object.
1205 *
1206 * This API applies to 1.2 and later only.
1207 */
1208
1209extern int
1210of_packet_in_setup_from_flow_add(of_packet_in_t *obj,
1211 of_flow_add_t *flow_add);
1212
1213
1214/**
1215 * @brief Set up the flow stats entry from the original add
1216 * @param obj The packet in message being updated
1217 * @param flow_add The flow_add message to use
1218 * @param effects Optional actions or instructions; see below.
1219 *
1220 * Initialize the following fields of obj to be identical
1221 * to what was originally on the wire from the flow_add object:
1222 * match
1223 * actions/instructions (effects)
1224 * cookie
1225 * priority
1226 * idle_timeout
1227 * hard_timeout
1228 *
Andreas Wundsam53256162013-05-02 14:05:53 -07001229 * Note that the actions/instructions of a flow may be modified by a
Rich Lanea06d0c32013-03-25 08:52:03 -07001230 * subsequent flow modify message. To facilitate implementations,
1231 * the "effects" parameter is provided. If effects is NULL, the
1232 * actions/instructions are taken from the flow_add message.
1233 * Otherwise, effects is coerced to the proper type (actions or
1234 * instructions) and used to init obj.
1235 */
1236
1237extern int
1238of_flow_stats_entry_setup_from_flow_add(of_flow_stats_entry_t *obj,
1239 of_flow_add_t *flow_add,
1240 of_object_t *effects);
1241""")
1242
Rich Lanea06d0c32013-03-25 08:52:03 -07001243################################################################
1244#
1245# List accessor code generation
1246#
1247# Currently these all implement copy on read semantics
1248#
1249################################################################
1250
1251def init_call(e_type, obj, ver, length, cw):
1252 """
1253 Generate the init call given the strings for params
1254 """
1255 hdr = "" # If inheritance type, coerce to hdr object
1256 obj_name = obj
1257 if e_type in type_maps.inheritance_map:
1258 hdr = "_header"
1259 obj_name = "(%s_header_t *)" % e_type + obj
1260
1261 return """\
1262%(e_type)s%(hdr)s_init(%(obj_name)s,
1263 %(ver)s, %(length)s, %(cw)s)\
1264""" % dict(e_type=e_type, hdr=hdr, obj_name=obj_name, ver=ver,
1265 length=length, cw=cw)
1266
1267def gen_list_first(out, cls, e_type):
1268 """
1269 Generate the body of a list_first operation
1270 @param cls The class name for which code is being generated
1271 @param e_type The element type of the list
1272 @param out The file to which to write
1273 """
1274 i_call = init_call(e_type, "obj", "list->version", "0", "1")
1275 if e_type in type_maps.inheritance_map:
1276 len_str = "obj->header.length"
1277 else:
1278 len_str = "obj->length"
1279
1280 out.write("""
1281/**
1282 * Associate an iterator with a list
1283 * @param list The list to iterate over
1284 * @param obj The list entry iteration pointer
1285 * @return OF_ERROR_RANGE if the list is empty (end of list)
1286 *
1287 * The obj instance is completely initialized. The caller is responsible
1288 * for cleaning up any wire buffers associated with obj before this call
1289 */
1290
1291int
1292%(cls)s_first(%(cls)s_t *list,
1293 %(e_type)s_t *obj)
1294{
1295 int rv;
1296
1297 %(i_call)s;
1298 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1299 return rv;
1300 }
1301""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1302
1303 # Special case flow_stats_entry lists
1304
1305 out.write("""
1306 of_object_wire_init((of_object_t *) obj, %(u_type)s,
1307 list->length);
1308 if (%(len_str)s == 0) {
1309 return OF_ERROR_PARSE;
1310 }
1311
1312 return rv;
1313}
1314""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1315
1316
1317def gen_bind(out, cls, m_name, m_type):
1318 """
1319 Generate the body of a bind function
1320 @param out The file to which to write
1321 @param cls The class name for which code is being generated
1322 @param m_name The name of the data member
1323 @param m_type The type of the data member
1324 """
1325
1326 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1327
1328 i_call = init_call(e_type, "child", "parent->version", "0", "1")
1329
1330 out.write("""
1331/**
1332 * Bind the child object to the parent object for read processing
1333 * @param parent The parent object
1334 * @param child The child object
1335 *
1336 * The child obj instance is completely initialized.
1337 */
1338
1339int
1340%(cls)s_%(m_name)_bind(%(cls)s_t *parent,
1341 %(e_type)s_t *child)
1342{
1343 int rv;
1344
1345 %(i_call)s;
1346
1347 /* Derive offset and length of child in parent */
Andreas Wundsam53256162013-05-02 14:05:53 -07001348 OF_TRY(of_object_child_attach(parent, child,
Rich Lanea06d0c32013-03-25 08:52:03 -07001349 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1350 return rv;
1351 }
1352""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1353
1354 # Special case flow_stats_entry lists
1355
1356 out.write("""
1357 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1358 list->length);
1359 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1360 return OF_ERROR_PARSE;
1361 }
1362
1363 return rv;
1364}
1365""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1366
1367
1368def gen_list_next(out, cls, e_type):
1369 """
1370 Generate the body of a list_next operation
1371 @param cls The class name for which code is being generated
1372 @param e_type The element type of the list
1373 @param out The file to which to write
1374 """
1375
1376 if e_type in type_maps.inheritance_map:
1377 len_str = "obj->header.length"
1378 else:
1379 len_str = "obj->length"
Andreas Wundsam53256162013-05-02 14:05:53 -07001380
Rich Lanea06d0c32013-03-25 08:52:03 -07001381 out.write("""
1382/**
1383 * Advance an iterator to the next element in a list
1384 * @param list The list being iterated
1385 * @param obj The list entry iteration pointer
1386 * @return OF_ERROR_RANGE if already at the last entry on the list
1387 *
1388 */
1389
1390int
1391%(cls)s_next(%(cls)s_t *list,
1392 %(e_type)s_t *obj)
1393{
1394 int rv;
1395
1396 if ((rv = of_list_next((of_object_t *)list, (of_object_t *)obj)) < 0) {
1397 return rv;
1398 }
1399
1400 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1401 list->length);
1402
1403 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1404 return OF_ERROR_PARSE;
1405 }
1406
1407 return rv;
1408}
1409""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1410
1411def gen_list_append(out, cls, e_type):
1412 """
1413 Generate the body of a list append functions
1414 @param cls The class name for which code is being generated
1415 @param e_type The element type of the list
1416 @param out The file to which to write
1417 """
1418
1419 out.write("""
1420/**
1421 * Set up to append an object of type %(e_type)s to an %(cls)s.
1422 * @param list The list that is prepared for append
1423 * @param obj Pointer to object to hold data to append
1424 *
1425 * The obj instance is completely initialized. The caller is responsible
1426 * for cleaning up any wire buffers associated with obj before this call.
1427 *
1428 * See the generic documentation for of_list_append_bind.
1429 */
1430
1431int
1432%(cls)s_append_bind(%(cls)s_t *list,
1433 %(e_type)s_t *obj)
1434{
1435 return of_list_append_bind((of_object_t *)list, (of_object_t *)obj);
1436}
1437
1438/**
1439 * Append an item to a %(cls)s list.
1440 *
1441 * This copies data from item and leaves item untouched.
1442 *
1443 * See the generic documentation for of_list_append.
1444 */
1445
1446int
1447%(cls)s_append(%(cls)s_t *list,
1448 %(e_type)s_t *item)
1449{
1450 return of_list_append((of_object_t *)list, (of_object_t *)item);
1451}
1452
1453""" % dict(cls=cls, e_type=e_type))
1454
1455def gen_list_accessors(out, cls):
1456 e_type = loxi_utils.list_to_entry_type(cls)
1457 gen_list_first(out, cls, e_type)
1458 gen_list_next(out, cls, e_type)
1459 gen_list_append(out, cls, e_type)
1460
1461################################################################
1462#
1463# Accessor Functions
1464#
1465################################################################
1466
Andreas Wundsam53256162013-05-02 14:05:53 -07001467
Rich Lanea06d0c32013-03-25 08:52:03 -07001468def gen_accessor_declarations(out):
1469 """
1470 Generate the declaration of each version independent accessor
1471
1472 @param out The file to which to write the decs
1473 """
1474
1475 out.write("""
1476/****************************************************************
1477 *
1478 * Unified, per-member accessor function declarations
1479 *
1480 ****************************************************************/
1481""")
1482 for cls in of_g.standard_class_order:
1483 if cls in type_maps.inheritance_map:
1484 continue
1485 out.write("\n/* Unified accessor functions for %s */\n" % cls)
1486 for m_name in of_g.ordered_members[cls]:
1487 if m_name in of_g.skip_members:
1488 continue
1489 m_type = loxi_utils.member_base_type(cls, m_name)
1490 base_name = "%s_%s" % (cls, m_name)
1491 gparams = ",\n ".join(param_list(cls, m_name, "get"))
1492 get_ret_type = accessor_return_type("get", m_type)
1493 sparams = ",\n ".join(param_list(cls, m_name, "set"))
1494 set_ret_type = accessor_return_type("set", m_type)
1495 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1496 bind_ret_type = accessor_return_type("bind", m_type)
1497
1498 if loxi_utils.type_is_of_object(m_type):
1499 # Generate bind accessors, but not get accessor
1500 out.write("""
1501extern %(set_ret_type)s %(base_name)s_set(
1502 %(sparams)s);
1503extern %(bind_ret_type)s %(base_name)s_bind(
1504 %(bparams)s);
1505extern %(m_type)s *%(cls)s_%(m_name)s_get(
1506 %(cls)s_t *obj);
1507""" % dict(base_name=base_name, sparams=sparams, bparams=bparams,
1508 m_name=m_name, m_type=m_type, cls=cls,
1509 set_ret_type=set_ret_type, bind_ret_type=bind_ret_type))
1510 else:
1511 out.write("""
1512extern %(set_ret_type)s %(base_name)s_set(
1513 %(sparams)s);
1514extern %(get_ret_type)s %(base_name)s_get(
1515 %(gparams)s);
1516""" % dict(base_name=base_name, gparams=gparams, sparams=sparams,
1517 get_ret_type=get_ret_type, set_ret_type=set_ret_type))
Andreas Wundsam53256162013-05-02 14:05:53 -07001518
Rich Lanea06d0c32013-03-25 08:52:03 -07001519 if loxi_utils.class_is_list(cls):
1520 e_type = loxi_utils.list_to_entry_type(cls)
1521 out.write("""
1522extern int %(cls)s_first(
1523 %(cls)s_t *list, %(e_type)s_t *obj);
1524extern int %(cls)s_next(
1525 %(cls)s_t *list, %(e_type)s_t *obj);
1526extern int %(cls)s_append_bind(
1527 %(cls)s_t *list, %(e_type)s_t *obj);
1528extern int %(cls)s_append(
1529 %(cls)s_t *list, %(e_type)s_t *obj);
1530
1531/**
1532 * Iteration macro for list of type %(cls)s
1533 * @param list Pointer to the list being iterated over of
1534 * type %(cls)s
1535 * @param elt Pointer to an element of type %(e_type)s
1536 * @param rv On exiting the loop will have the value OF_ERROR_RANGE.
1537 */
1538#define %(u_cls)s_ITER(list, elt, rv) \\
1539 for ((rv) = %(cls)s_first((list), (elt)); \\
1540 (rv) == OF_ERROR_NONE; \\
1541 (rv) = %(cls)s_next((list), (elt)))
1542""" % dict(u_cls=cls.upper(), cls=cls, e_type=e_type))
1543
1544
1545def wire_accessor(m_type, a_type):
1546 """
1547 Returns the name of the a_type accessor for low level wire buff offset
1548 @param m_type The member type
1549 @param a_type The accessor type (set or get)
1550 """
1551 # Strip off _t if present
1552 if m_type in of_g.of_base_types:
1553 m_type = of_g.of_base_types[m_type]["short_name"]
1554 if m_type in of_g.of_mixed_types:
1555 m_type = of_g.of_mixed_types[m_type]["short_name"]
1556 if m_type[-2:] == "_t":
1557 m_type = m_type[:-2]
1558 if m_type == "octets":
1559 m_type = "octets_data"
1560 return "of_wire_buffer_%s_%s" % (m_type, a_type)
1561
Rich Lane713d9282013-12-30 15:21:35 -08001562def get_len_macro(cls, m_name, m_type, version):
Rich Lanea06d0c32013-03-25 08:52:03 -07001563 """
1564 Get the length macro for m_type in cls
1565 """
1566 if m_type.find("of_match") == 0:
1567 return "_WIRE_MATCH_PADDED_LEN(obj, offset)"
1568 if m_type.find("of_list_oxm") == 0:
1569 return "wire_match_len(obj, 0) - 4"
1570 if loxi_utils.class_is_tlv16(m_type):
1571 return "_TLV16_LEN(obj, offset)"
1572 if cls == "of_packet_out" and m_type == "of_list_action_t":
1573 return "_PACKET_OUT_ACTION_LEN(obj)"
Rich Lane713d9282013-12-30 15:21:35 -08001574 if cls == "of_bsn_gentable_entry_add" and m_name == "key":
1575 return "of_object_u16_get(obj, 18)"
1576 if cls == "of_bsn_gentable_entry_desc_stats_entry" and m_name == "key":
1577 return "of_object_u16_get(obj, 2)"
1578 if cls == "of_bsn_gentable_entry_stats_entry" and m_name == "key":
1579 return "of_object_u16_get(obj, 2)"
Rich Lanea06d0c32013-03-25 08:52:03 -07001580 # Default is everything to the end of the object
1581 return "_END_LEN(obj, offset)"
1582
1583def gen_accessor_offsets(out, cls, m_name, version, a_type, m_type, offset):
1584 """
1585 Generate the sub-object offset and length calculations for accessors
1586 @param out File being written
1587 @param m_name Name of member
1588 @param version Wire version being processed
1589 @param a_type The accessor type (set or get)
1590 @param m_type The original member type
1591 @param offset The offset of the object or -1 if not fixed
1592 """
1593 # determine offset
1594 o_str = "%d" % offset # Default is fixed length
1595 if offset == -1:
1596 # There are currently 4 special cases for this
1597 # In general, get offset and length of predecessor
1598 if (loxi_utils.cls_is_flow_mod(cls) and m_name == "instructions"):
1599 pass
1600 elif (cls == "of_flow_stats_entry" and m_name == "instructions"):
1601 pass
1602 elif (cls == "of_packet_in" and m_name == "data"):
1603 pass
1604 elif (cls == "of_packet_out" and m_name == "data"):
1605 pass
Rich Lane713d9282013-12-30 15:21:35 -08001606 elif (cls == "of_bsn_gentable_entry_add" and m_name == "value"):
1607 pass
1608 elif (cls == "of_bsn_gentable_entry_desc_stats_entry" and m_name == "value"):
1609 pass
1610 elif (cls == "of_bsn_gentable_entry_stats_entry" and m_name == "stats"):
1611 pass
Rich Lanea06d0c32013-03-25 08:52:03 -07001612 else:
1613 debug("Error: Unknown member with offset == -1")
1614 debug(" cls %s, m_name %s, version %d" % (cls, m_name, version))
1615 sys.exit(1)
1616 o_str = "_%s_%s_OFFSET(obj)" % (cls.upper()[3:], m_name.upper())
1617
1618 out.write("""\
1619 offset = %s;
1620""" % o_str);
1621
1622 # This could be moved to main body but for version check
1623 if not loxi_utils.type_is_scalar(m_type):
1624 if loxi_utils.class_is_var_len(m_type[:-2], version) or \
1625 m_type == "of_match_t":
Rich Lane713d9282013-12-30 15:21:35 -08001626 len_macro = get_len_macro(cls, m_name, m_type, version)
Rich Lanea06d0c32013-03-25 08:52:03 -07001627 else:
1628 len_macro = "%d" % of_g.base_length[(m_type[:-2], version)]
1629 out.write(" cur_len = %s;\n" % len_macro)
1630 out.write(" break;\n")
1631
1632def length_of(m_type, version):
1633 """
1634 Return the length of a type based on the version
1635 """
1636 if m_type in of_g.of_mixed_types:
1637 m_type = of_g.of_mixed_types[m_type][version]
1638 if m_type in of_g.of_base_types:
1639 return of_g.of_base_types[m_type]["bytes"]
1640 if (m_type[:-2], version) in of_g.base_length:
1641 return of_g.base_length[(m_type[:-2], version)]
1642 print "Unknown length request", m_type, version
1643 sys.exit(1)
Andreas Wundsam53256162013-05-02 14:05:53 -07001644
Rich Lanea06d0c32013-03-25 08:52:03 -07001645
1646def gen_get_accessor_body(out, cls, m_type, m_name):
1647 """
1648 Generate the common operations for a get accessor
1649 """
1650 if loxi_utils.type_is_scalar(m_type):
1651 ver = "" # See if version required for scalar update
1652 if m_type in of_g.of_mixed_types:
1653 ver = "ver, "
1654 out.write("""\
1655 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s);
1656""" % dict(wa=wire_accessor(m_type, "get"), ver=ver, m_name=m_name))
1657
1658 if m_type == "of_port_no_t":
1659 out.write(" OF_PORT_NO_VALUE_CHECK(*%s, ver);\n" % m_name)
1660 elif m_type == "of_octets_t":
1661 out.write("""\
Rich Lanee57f0432014-02-19 10:31:53 -08001662 LOCI_ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
Rich Lanea06d0c32013-03-25 08:52:03 -07001663 %(m_name)s->bytes = cur_len;
1664 %(m_name)s->data = OF_WIRE_BUFFER_INDEX(wbuf, abs_offset);
1665""" % dict(m_name=m_name))
1666 elif m_type == "of_match_t":
1667 out.write("""
Rich Lanee57f0432014-02-19 10:31:53 -08001668 LOCI_ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
Rich Lanea06d0c32013-03-25 08:52:03 -07001669 match_octets.bytes = cur_len;
1670 match_octets.data = OF_OBJECT_BUFFER_INDEX(obj, offset);
1671 OF_TRY(of_match_deserialize(ver, %(m_name)s, &match_octets));
1672""" % dict(m_name=m_name))
1673 else:
1674 out.write("""
1675 /* Initialize child */
1676 %(m_type)s_init(%(m_name)s, obj->version, 0, 1);
1677 /* Attach to parent */
1678 %(m_name)s->parent = (of_object_t *)obj;
1679 %(m_name)s->wire_object.wbuf = obj->wire_object.wbuf;
1680 %(m_name)s->wire_object.obj_offset = abs_offset;
1681 %(m_name)s->wire_object.owned = 0;
1682 %(m_name)s->length = cur_len;
1683""" % dict(m_type=m_type[:-2], m_name=m_name))
1684
1685
1686def gen_set_accessor_body(out, cls, m_type, m_name):
1687 """
1688 Generate the contents of a set accessor
1689 """
1690 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1691 ver = "" # See if version required for scalar update
1692 if m_type in of_g.of_mixed_types:
1693 ver = "ver, "
1694 cur_len = "" # See if version required for scalar update
1695 if m_type == "of_octets_t":
1696 cur_len = ", cur_len"
1697 out.write("""\
1698 new_len = %(m_name)s->bytes;
1699 of_wire_buffer_grow(wbuf, abs_offset + (new_len - cur_len));
1700""" % dict(m_name=m_name))
1701 out.write("""\
1702 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s%(cur_len)s);
1703""" % dict(wa=wire_accessor(m_type, "set"), ver=ver, cur_len=cur_len,
1704 m_name=m_name))
1705
1706 elif m_type == "of_match_t":
1707 out.write("""
1708 /* Match object */
1709 OF_TRY(of_match_serialize(ver, %(m_name)s, &match_octets));
1710 new_len = match_octets.bytes;
1711 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1712 match_octets.data, new_len);
1713 /* Free match serialized octets */
1714 FREE(match_octets.data);
1715""" % dict(m_name=m_name))
1716
1717 else: # Other object type
1718 out.write("\n /* LOCI object type */")
1719 # Need to special case OXM list
1720 out.write("""
1721 new_len = %(m_name)s->length;
1722 /* If underlying buffer already shared; nothing to do */
1723 if (obj->wire_object.wbuf == %(m_name)s->wire_object.wbuf) {
1724 of_wire_buffer_grow(wbuf, abs_offset + new_len);
1725 /* Verify that the offsets are correct */
Rich Lanee57f0432014-02-19 10:31:53 -08001726 LOCI_ASSERT(abs_offset == OF_OBJECT_ABSOLUTE_OFFSET(%(m_name)s, 0));
1727 /* LOCI_ASSERT(new_len == cur_len); */ /* fixme: may fail for OXM lists */
Rich Lanea06d0c32013-03-25 08:52:03 -07001728 return %(ret_success)s;
1729 }
1730
1731 /* Otherwise, replace existing object in data buffer */
1732 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1733 OF_OBJECT_BUFFER_INDEX(%(m_name)s, 0), new_len);
1734""" % dict(m_name=m_name, ret_success=accessor_return_success("set", m_type)))
1735
1736 if not loxi_utils.type_is_scalar(m_type):
1737 if cls == "of_packet_out" and m_type == "of_list_action_t":
1738 out.write("""
1739 /* Special case for setting action lengths */
1740 _PACKET_OUT_ACTION_LEN_SET(obj, %(m_name)s->length);
1741""" % dict(m_name=m_name))
Rich Lane713d9282013-12-30 15:21:35 -08001742 elif cls == "of_bsn_gentable_entry_add" and m_name == "key":
1743 out.write("""
1744 /* Special case for setting key length */
1745 of_object_u16_set(obj, 18, %(m_name)s->length);
1746""" % dict(m_name=m_name))
1747 elif cls in ["of_bsn_gentable_entry_desc_stats_entry", "of_bsn_gentable_entry_stats_entry"] and m_name == "key":
1748 out.write("""
1749 /* Special case for setting key length */
1750 of_object_u16_set(obj, 2, %(m_name)s->length);
1751""" % dict(m_name=m_name))
Rich Lanea06d0c32013-03-25 08:52:03 -07001752 elif m_type not in ["of_match_t", "of_octets_t"]:
1753 out.write("""
1754 /* @fixme Shouldn't this precede copying value's data to buffer? */
1755 if (%(m_name)s->wire_length_set != NULL) {
1756 %(m_name)s->wire_length_set((of_object_t *)%(m_name)s, %(m_name)s->length);
1757 }
1758""" % dict(m_name=m_name))
1759 out.write("""
1760 /* Not scalar, update lengths if needed */
1761 delta = new_len - cur_len;
1762 if (delta != 0) {
1763 /* Update parent(s) */
1764 of_object_parent_length_update((of_object_t *)obj, delta);
1765 }
1766""")
1767
1768def obj_assert_check(cls):
1769 """
1770 The body of the assert check for an accessor
1771 We allow all versions of add/delete/modify to use the same accessors
1772 """
1773 if cls in ["of_flow_modify", "of_flow_modify_strict",
1774 "of_flow_delete", "of_flow_delete_strict",
1775 "of_flow_add"]:
1776 return "IS_FLOW_MOD_SUBTYPE(obj->object_id)"
1777 else:
1778 return "obj->object_id == %s" % cls.upper()
1779
1780def gen_of_object_get(out, cls, m_name, m_type):
1781 sub_cls = m_type[:-2]
1782 out.write("""
1783/**
Andreas Wundsam53256162013-05-02 14:05:53 -07001784 * Create a copy of %(m_name)s into a new variable of type %(m_type)s from
Rich Lanea06d0c32013-03-25 08:52:03 -07001785 * a %(cls)s instance.
1786 *
1787 * @param obj Pointer to the source of type %(cls)s_t
1788 * @returns A pointer to a new instance of type %(m_type)s whose contents
1789 * match that of %(m_name)s from source
1790 * @returns NULL if an error occurs
1791 */
1792%(m_type)s *
1793%(cls)s_%(m_name)s_get(%(cls)s_t *obj) {
1794 %(m_type)s _%(m_name)s;
1795 %(m_type)s *_%(m_name)s_ptr;
1796
1797 %(cls)s_%(m_name)s_bind(obj, &_%(m_name)s);
1798 _%(m_name)s_ptr = (%(m_type)s *)of_object_dup(&_%(m_name)s);
1799 return _%(m_name)s_ptr;
1800}
1801""" % dict(m_name=m_name, m_type=m_type, cls=cls, sub_cls=sub_cls))
1802
1803def gen_unified_acc_body(out, cls, m_name, ver_type_map, a_type, m_type):
1804 """
1805 Generate the body of a set or get accessor function
1806
1807 @param out The file to which to write the decs
1808 @param cls The class name
1809 @param m_name The member name
1810 @param ver_type_map Maps (type, offset) pairs to a list of versions
1811 @param a_type The accessor type, set or get
1812 @param m_type The original member type
1813
1814 The type values in ver_type_map are now ignored as we've pushed down
1815 the type munging to the lower level.
1816
1817 This is unified because the version switch case processing is the
1818 same for both set and get
1819 """
1820 out.write("""{
1821 of_wire_buffer_t *wbuf;
1822 int offset = 0; /* Offset of value relative to the start obj */
1823 int abs_offset; /* Offset of value relative to start of wbuf */
1824 of_version_t ver;
1825""")
1826
1827 if not loxi_utils.type_is_scalar(m_type):
1828 out.write("""\
1829 int cur_len = 0; /* Current length of object data */
1830""")
1831 if a_type == "set":
1832 out.write("""\
1833 int new_len, delta; /* For set, need new length and delta */
1834""")
1835
1836 # For match, need octet string for set/get
1837 if m_type == "of_match_t":
1838 out.write("""\
1839 of_octets_t match_octets; /* Serialized string for match */
1840""")
1841
1842 out.write("""
Rich Lanee57f0432014-02-19 10:31:53 -08001843 LOCI_ASSERT(%(assert_str)s);
Rich Lanea06d0c32013-03-25 08:52:03 -07001844 ver = obj->version;
1845 wbuf = OF_OBJECT_TO_WBUF(obj);
Rich Lanee57f0432014-02-19 10:31:53 -08001846 LOCI_ASSERT(wbuf != NULL);
Rich Lanea06d0c32013-03-25 08:52:03 -07001847
1848 /* By version, determine offset and current length (where needed) */
1849 switch (ver) {
1850""" % dict(assert_str=obj_assert_check(cls)))
1851
1852 for first in sorted(ver_type_map):
1853 (prev_t, prev_o) = ver_type_map[first]
1854 prev_len = length_of(prev_t, first)
1855 prev = first
1856 out.write(" case %s:\n" % of_g.wire_ver_map[first])
1857 break
1858
1859 for next in sorted(ver_type_map):
1860 if next == first:
1861 continue
1862
1863 (t, o) = ver_type_map[next]
1864 cur_len = length_of(t, next)
1865 if o == prev_o and cur_len == prev_len:
1866 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1867 continue
1868 gen_accessor_offsets(out, cls, m_name, prev, a_type, m_type, prev_o)
1869 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1870 (prev_t, prev_o, prev_len, prev) = (t, o, cur_len, next)
1871
1872 gen_accessor_offsets(out, cls, m_name, next, a_type, m_type, prev_o)
1873 out.write("""\
1874 default:
Rich Lanee57f0432014-02-19 10:31:53 -08001875 LOCI_ASSERT(0);
Rich Lanea06d0c32013-03-25 08:52:03 -07001876 }
1877
1878 abs_offset = OF_OBJECT_ABSOLUTE_OFFSET(obj, offset);
Rich Lanee57f0432014-02-19 10:31:53 -08001879 LOCI_ASSERT(abs_offset >= 0);
Rich Lanea06d0c32013-03-25 08:52:03 -07001880""")
1881 if not loxi_utils.type_is_scalar(m_type):
Rich Lanee57f0432014-02-19 10:31:53 -08001882 out.write(" LOCI_ASSERT(cur_len >= 0 && cur_len < 64 * 1024);\n")
Rich Lanea06d0c32013-03-25 08:52:03 -07001883
1884 # Now generate the common accessor code
1885 if a_type in ["get", "bind"]:
1886 gen_get_accessor_body(out, cls, m_type, m_name)
1887 else:
1888 gen_set_accessor_body(out, cls, m_type, m_name)
1889
1890 out.write("""
1891 OF_LENGTH_CHECK_ASSERT(obj);
1892
1893 return %s;
1894}
1895""" % accessor_return_success(a_type, m_type))
1896
1897def gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map):
1898 """
1899 For generating the bind call for OF sub-objects
1900 """
1901
1902 params = ",\n ".join(param_list(cls, m_name, "bind"))
1903 out.write("""
1904/**
1905 * Bind an object of type %(m_type)s to the parent of type %(cls)s for
1906 * member %(m_name)s
1907 * @param obj Pointer to an object of type %(cls)s.
1908 * @param %(m_name)s Pointer to the child object of type
1909 * %(m_type)s to be filled out.
1910 * \ingroup %(cls)s
1911 *
1912 * The parameter %(m_name)s is filled out to point to the same underlying
1913 * wire buffer as its parent.
1914 *
1915 */
1916""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1917
1918 ret_type = accessor_return_type("bind", m_type)
1919 out.write("%s\n%s_%s_bind(\n %s)\n" % (ret_type, cls, m_name, params))
1920 gen_unified_acc_body(out, cls, m_name, ver_type_map, "bind", m_type)
1921
1922def gen_get_accessor(out, cls, m_name, m_type, ver_type_map):
1923 """
1924 For generating the get call for non- OF sub-objects
1925 """
1926 params = ",\n ".join(param_list(cls, m_name, "get"))
1927 out.write("""
1928/**
1929 * Get %(m_name)s from an object of type %(cls)s.
1930 * @param obj Pointer to an object of type %(cls)s.
1931 * @param %(m_name)s Pointer to the child object of type
1932 * %(m_type)s to be filled out.
1933 *
1934 */
1935""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1936
1937 ret_type = accessor_return_type("get", m_type)
1938 out.write("%s\n%s_%s_get(\n %s)\n" % (ret_type, cls, m_name, params))
1939 gen_unified_acc_body(out, cls, m_name, ver_type_map, "get", m_type)
1940
Rich Lanece2e4642013-12-15 12:05:45 -08001941def gen_accessor_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07001942 for m_name in of_g.ordered_members[cls]:
1943 if m_name in of_g.skip_members:
1944 continue
1945 m_type = loxi_utils.member_base_type(cls, m_name)
1946 ver_type_map = field_ver_get(cls, m_name)
1947
1948 # Generate get/bind pending on member type
1949 # FIXME: Does this do the right thing for match?
1950 if loxi_utils.type_is_of_object(m_type):
1951 gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map)
1952 gen_of_object_get(out, cls, m_name, m_type)
1953 else:
1954 gen_get_accessor(out, cls, m_name, m_type, ver_type_map)
1955
1956 # Now generate set accessor for all objects
1957 params = ",\n ".join(param_list(cls, m_name, "set"))
1958 out.write("""
1959/**
1960 * Set %(m_name)s in an object of type %(cls)s.
1961 * @param obj Pointer to an object of type %(cls)s.
1962""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1963 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1964 out.write("""\
1965 * @param %(m_name)s The value to write into the object
1966 */
1967""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1968 else:
1969 out.write("""\
1970 * @param %(m_name)s Pointer to the child of type %(m_type)s.
1971 *
1972 * If the child's wire buffer is the same as the parent's, then
1973 * nothing is done as the changes have already been registered in the
1974 * parent. Otherwise, the data in the child's wire buffer is inserted
1975 * into the parent's and the appropriate lengths are updated.
1976 */
1977""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1978 ret_type = accessor_return_type("set", m_type)
1979 out.write("%s\n%s_%s_set(\n %s)\n" % (ret_type, cls, m_name, params))
1980 gen_unified_acc_body(out, cls, m_name, ver_type_map, "set", m_type)
1981
Rich Lanea06d0c32013-03-25 08:52:03 -07001982################################################################
1983#
1984# New/Delete Function Definitions
1985#
1986################################################################
1987
1988
1989################################################################
1990# First, some utility functions for new/delete
1991################################################################
1992
1993def del_function_proto(cls):
1994 """
1995 Return the prototype for the delete operator for the given class
1996 @param cls The class name
1997 """
1998 fn = "void\n"
1999 return fn
2000
2001
Rich Lanea06d0c32013-03-25 08:52:03 -07002002################################################################
2003# Routines to generate the body of new/delete functions
2004################################################################
2005
2006def gen_init_fn_body(cls, out):
2007 """
2008 Generate function body for init function
2009 @param cls The class name for the function
2010 @param out The file to which to write
2011 """
2012 if cls in type_maps.inheritance_map:
2013 param = "obj_p"
2014 else:
2015 param = "obj"
2016
2017 out.write("""
2018/**
2019 * Initialize an object of type %(cls)s.
2020 *
2021 * @param obj Pointer to the object to initialize
2022 * @param version The wire version to use for the object
2023 * @param bytes How many bytes in the object
2024 * @param clean_wire Boolean: If true, clear the wire object control struct
2025 *
2026 * If bytes < 0, then the default fixed length is used for the object
2027 *
2028 * This is a "coerce" function that sets up the pointers for the
Andreas Wundsam53256162013-05-02 14:05:53 -07002029 * accessors properly.
Rich Lanea06d0c32013-03-25 08:52:03 -07002030 *
2031 * If anything other than 0 is passed in for the buffer size, the underlying
2032 * wire buffer will have 'grow' called.
2033 */
2034
2035void
2036%(cls)s_init(%(cls)s_t *%(param)s,
2037 of_version_t version, int bytes, int clean_wire)
2038{
2039""" % dict(cls=cls, param=param))
2040
2041 # Use an extra pointer to deal with inheritance classes
2042 if cls in type_maps.inheritance_map:
2043 out.write("""\
2044 %s_header_t *obj;
2045
2046 obj = &obj_p->header; /* Need instantiable subclass */
2047""" % cls)
2048
2049 out.write("""
Rich Lanee57f0432014-02-19 10:31:53 -08002050 LOCI_ASSERT(of_object_fixed_len[version][%(enum)s] >= 0);
Rich Lanea06d0c32013-03-25 08:52:03 -07002051 if (clean_wire) {
2052 MEMSET(obj, 0, sizeof(*obj));
2053 }
2054 if (bytes < 0) {
Rich Lanef70be942013-07-18 13:33:14 -07002055 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002056 }
2057 obj->version = version;
2058 obj->length = bytes;
2059 obj->object_id = %(enum)s;
2060""" % dict(cls=cls, enum=enum_name(cls)))
2061 gen_coerce_ops(out, cls)
2062
2063 out.write("""
2064 /* Grow the wire buffer */
2065 if (obj->wire_object.wbuf != NULL) {
2066 int tot_bytes;
2067
2068 tot_bytes = bytes + obj->wire_object.obj_offset;
2069 of_wire_buffer_grow(obj->wire_object.wbuf, tot_bytes);
2070 }
2071}
2072
2073""")
2074
2075## @fixme This should also be updated once there is a map from
2076# class instance to wire length/type accessors
2077def gen_wire_push_fn(cls, out):
2078 """
2079 Generate the calls to push values into the wire buffer
2080 """
2081 if type_maps.class_is_virtual(cls):
2082 print "Push fn gen called for virtual class " + cls
2083 return
2084
2085 out.write("""
2086/**
2087 * Helper function to push values into the wire buffer
2088 */
2089static inline int
2090%(cls)s_push_wire_values(%(cls)s_t *obj)
2091{
2092""" % dict(cls=cls))
2093
Rich Lanebdd8e292013-12-06 17:37:39 -08002094 import loxi_globals
2095 uclass = loxi_globals.unified.class_by_name(cls)
2096 if uclass and not uclass.virtual and uclass.has_type_members:
2097 out.write("""
2098 %(cls)s_push_wire_types(obj);
2099""" % dict(cls=cls))
2100
Rich Lanea06d0c32013-03-25 08:52:03 -07002101 if loxi_utils.class_is_message(cls):
2102 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002103 /* Message obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002104 of_message_t msg;
2105
2106 if ((msg = OF_OBJECT_TO_MESSAGE(obj)) != NULL) {
Rich Lanea06d0c32013-03-25 08:52:03 -07002107 of_message_length_set(msg, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002108 }
2109""" % dict(name = enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002110
Rich Lanea06d0c32013-03-25 08:52:03 -07002111 else: # Not a message
2112 if loxi_utils.class_is_tlv16(cls):
2113 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002114 /* TLV obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002115 of_tlv16_wire_length_set((of_object_t *)obj, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002116""" % dict(enum=enum_name(cls)))
Rich Lanea06d0c32013-03-25 08:52:03 -07002117
Rich Lanea06d0c32013-03-25 08:52:03 -07002118 if loxi_utils.class_is_u16_len(cls) or cls == "of_packet_queue":
2119 out.write("""
2120 obj->wire_length_set((of_object_t *)obj, obj->length);
2121""")
2122
2123 if cls == "of_meter_stats":
2124 out.write("""
2125 of_meter_stats_wire_length_set((of_object_t *)obj, obj->length);
2126""" % dict(enum=enum_name(cls)))
2127
2128 out.write("""
2129 return OF_ERROR_NONE;
2130}
2131""")
2132
2133def gen_new_fn_body(cls, out):
2134 """
2135 Generate function body for new function
2136 @param cls The class name for the function
2137 @param out The file to which to write
2138 """
2139
2140 out.write("""
2141/**
2142 * \\defgroup %(cls)s %(cls)s
2143 */
2144""" % dict(cls=cls))
2145
2146 if not type_maps.class_is_virtual(cls):
2147 gen_wire_push_fn(cls, out)
2148
2149 out.write("""
2150/**
2151 * Create a new %(cls)s object
2152 *
2153 * @param version The wire version to use for the object
2154 * @return Pointer to the newly create object or NULL on error
2155 *
2156 * Initializes the new object with it's default fixed length associating
2157 * a new underlying wire buffer.
2158 *
2159 * Use new_from_message to bind an existing message to a message object,
2160 * or a _get function for non-message objects.
2161 *
2162 * \\ingroup %(cls)s
2163 */
2164
2165%(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002166%(cls)s_new(of_version_t version)
Rich Lanea06d0c32013-03-25 08:52:03 -07002167{
2168 %(cls)s_t *obj;
2169 int bytes;
2170
Rich Lanef70be942013-07-18 13:33:14 -07002171 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002172
2173 /* Allocate a maximum-length wire buffer assuming we'll be appending to it. */
2174 if ((obj = (%(cls)s_t *)of_object_new(OF_WIRE_BUFFER_MAX_LENGTH)) == NULL) {
2175 return NULL;
2176 }
2177
2178 %(cls)s_init(obj, version, bytes, 0);
2179""" % dict(cls=cls, enum=enum_name(cls)))
2180 if not type_maps.class_is_virtual(cls):
2181 out.write("""
2182 if (%(cls)s_push_wire_values(obj) < 0) {
2183 FREE(obj);
2184 return NULL;
2185 }
2186""" % dict(cls=cls))
2187
2188 match_offset = v3_match_offset_get(cls)
2189 if match_offset >= 0:
2190 # Init length field for match object
2191 out.write("""
2192 /* Initialize match TLV for 1.2 */
2193 /* FIXME: Check 1.3 below */
2194 if ((version == OF_VERSION_1_2) || (version == OF_VERSION_1_3)) {
2195 of_object_u16_set((of_object_t *)obj, %(match_offset)d + 2, 4);
2196 }
2197""" % dict(match_offset=match_offset))
2198 out.write("""
2199 return obj;
2200}
Rich Lanea06d0c32013-03-25 08:52:03 -07002201""" % dict(cls=cls))
2202
2203
2204def gen_from_message_fn_body(cls, out):
2205 """
2206 Generate function body for from_message function
2207 @param cls The class name for the function
2208 @param out The file to which to write
2209 """
2210 out.write("""
2211/**
2212 * Create a new %(cls)s object and bind it to an existing message
2213 *
2214 * @param msg The message to bind the new object to
2215 * @return Pointer to the newly create object or NULL on error
2216 *
2217 * \ingroup %(cls)s
2218 */
2219
2220%(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002221%(cls)s_new_from_message(of_message_t msg)
Rich Lanea06d0c32013-03-25 08:52:03 -07002222{
2223 %(cls)s_t *obj = NULL;
2224 of_version_t version;
2225 int length;
2226
2227 if (msg == NULL) return NULL;
2228
2229 version = of_message_version_get(msg);
2230 if (!OF_VERSION_OKAY(version)) return NULL;
2231
2232 length = of_message_length_get(msg);
2233
2234 if ((obj = (%(cls)s_t *)of_object_new(-1)) == NULL) {
2235 return NULL;
2236 }
2237
2238 %(cls)s_init(obj, version, 0, 0);
2239
2240 if ((of_object_buffer_bind((of_object_t *)obj, OF_MESSAGE_TO_BUFFER(msg),
2241 length, OF_MESSAGE_FREE_FUNCTION)) < 0) {
2242 FREE(obj);
2243 return NULL;
2244 }
2245 obj->length = length;
2246 obj->version = version;
2247
2248 return obj;
2249}
Rich Lanea06d0c32013-03-25 08:52:03 -07002250""" % dict(cls=cls))
2251
2252
2253################################################################
2254# Now the top level generator functions
2255################################################################
2256
2257def gen_new_function_declarations(out):
2258 """
2259 Gerenate the header file declarations for new operators for all classes
2260 @param out The file to which to write the decs
2261 """
2262
2263 out.write("""
2264/****************************************************************
2265 *
2266 * New operator declarations
2267 *
2268 * _new: Create a new object for writing; includes init
2269 * _new_from_message: Create a new instance of the object and bind the
2270 * message data to the object
2271 * _init: Initialize and optionally allocate buffer space for an
2272 * automatic instance
2273 *
2274 * _new and _from_message require a delete operation to be called
2275 * on the object.
2276 *
2277 ****************************************************************/
2278""")
Rich Lanea06d0c32013-03-25 08:52:03 -07002279
2280 for cls in of_g.standard_class_order:
2281 out.write("""
2282extern %(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002283 %(cls)s_new(of_version_t version);
Rich Lanea06d0c32013-03-25 08:52:03 -07002284""" % dict(cls=cls))
2285 if loxi_utils.class_is_message(cls):
2286 out.write("""extern %(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002287 %(cls)s_new_from_message(of_message_t msg);
Rich Lanea06d0c32013-03-25 08:52:03 -07002288""" % dict(cls=cls))
2289 out.write("""extern void %(cls)s_init(
2290 %(cls)s_t *obj, of_version_t version, int bytes, int clean_wire);
2291""" % dict(cls=cls))
2292
2293 out.write("""
2294/****************************************************************
2295 *
2296 * Delete operator static inline definitions.
2297 * These are here for type checking purposes only
2298 *
2299 ****************************************************************/
2300""")
2301 for cls in of_g.standard_class_order:
2302# if cls in type_maps.inheritance_map:
2303# continue
2304 out.write("""
2305/**
2306 * Delete an object of type %(cls)s_t
2307 * @param obj An instance of type %(cls)s_t
2308 *
2309 * \ingroup %(cls)s
2310 */
2311static inline void
2312%(cls)s_delete(%(cls)s_t *obj) {
2313 of_object_delete((of_object_t *)(obj));
2314}
2315""" % dict(cls=cls))
2316
2317 out.write("""
2318typedef void (*of_object_init_f)(of_object_t *obj, of_version_t version,
2319 int bytes, int clean_wire);
Rich Laneb157b0f2013-03-27 13:55:28 -07002320extern const of_object_init_f of_object_init_map[];
Rich Lanea06d0c32013-03-25 08:52:03 -07002321""")
2322
2323 out.write("""
2324/****************************************************************
2325 *
2326 * Function pointer initialization functions
2327 * These are part of the "coerce" type casting for objects
2328 *
2329 ****************************************************************/
2330""")
2331
2332#
2333# @fixme Not clear that these should all be set for virtual fns
2334#
2335# @fixme Clean up. should have a (language specific) map from class
2336# to length and type get/set functions
2337#
2338
2339def gen_coerce_ops(out, cls):
2340 out.write("""
2341 /* Set up the object's function pointers */
2342""")
2343
Rich Lane92feca82013-12-10 15:57:13 -08002344 uclass = loxi_globals.unified.class_by_name(cls)
2345 if uclass and not uclass.virtual and uclass.has_type_members:
2346 out.write("""
2347 obj->wire_type_set = %(cls)s_push_wire_types;
2348""" % dict(cls=cls))
2349
Rich Lanea06d0c32013-03-25 08:52:03 -07002350 if loxi_utils.class_is_message(cls):
2351 out.write("""
2352 obj->wire_length_get = of_object_message_wire_length_get;
2353 obj->wire_length_set = of_object_message_wire_length_set;
2354""")
2355 else:
2356 if loxi_utils.class_is_tlv16(cls):
2357 if not (cls in type_maps.inheritance_map): # Don't set for super
2358 out.write("""
2359 obj->wire_length_set = of_tlv16_wire_length_set;
Rich Lanea06d0c32013-03-25 08:52:03 -07002360""")
2361 out.write("""
2362 obj->wire_length_get = of_tlv16_wire_length_get;
2363""")
2364 if loxi_utils.class_is_action(cls):
2365 out.write("""
2366 obj->wire_type_get = of_action_wire_object_id_get;
2367""")
2368 if loxi_utils.class_is_action_id(cls):
2369 out.write("""
2370 obj->wire_type_get = of_action_id_wire_object_id_get;
2371""")
2372 if loxi_utils.class_is_instruction(cls):
2373 out.write("""
2374 obj->wire_type_get = of_instruction_wire_object_id_get;
2375""")
Jonathan Stout83cedcc2014-03-03 16:38:00 -05002376 if loxi_utils.class_is_instruction_id(cls):
2377 out.write("""
2378 obj->wire_type_get = of_instruction_id_wire_object_id_get;
2379""")
Rich Lanea06d0c32013-03-25 08:52:03 -07002380 if loxi_utils.class_is_queue_prop(cls):
2381 out.write("""
2382 obj->wire_type_get = of_queue_prop_wire_object_id_get;
2383""")
2384 if loxi_utils.class_is_table_feature_prop(cls):
2385 out.write("""
2386 obj->wire_type_get = of_table_feature_prop_wire_object_id_get;
2387""")
2388 if loxi_utils.class_is_meter_band(cls):
2389 out.write("""
2390 obj->wire_type_get = of_meter_band_wire_object_id_get;
2391""")
2392 if loxi_utils.class_is_hello_elem(cls):
2393 out.write("""
2394 obj->wire_type_get = of_hello_elem_wire_object_id_get;
2395""")
Rich Lane713d9282013-12-30 15:21:35 -08002396 if loxi_utils.class_is_bsn_tlv(cls):
2397 out.write("""
2398 obj->wire_type_get = of_bsn_tlv_wire_object_id_get;
2399""")
Rich Lanea06d0c32013-03-25 08:52:03 -07002400 if loxi_utils.class_is_oxm(cls):
2401 out.write("""
2402 obj->wire_length_get = of_oxm_wire_length_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002403 obj->wire_type_get = of_oxm_wire_object_id_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002404""")
2405 if loxi_utils.class_is_u16_len(cls):
2406 out.write("""
2407 obj->wire_length_get = of_u16_len_wire_length_get;
2408 obj->wire_length_set = of_u16_len_wire_length_set;
2409""")
2410 if cls == "of_packet_queue":
2411 out.write("""
2412 obj->wire_length_get = of_packet_queue_wire_length_get;
2413 obj->wire_length_set = of_packet_queue_wire_length_set;
2414""")
2415# if cls == "of_list_meter_band_stats":
2416# out.write("""
2417# obj->wire_length_get = of_list_meter_band_stats_wire_length_get;
2418#""")
2419 if cls == "of_meter_stats":
2420 out.write("""
2421 obj->wire_length_get = of_meter_stats_wire_length_get;
2422 obj->wire_length_set = of_meter_stats_wire_length_set;
2423""")
2424
Rich Laneb604e332013-12-15 13:23:51 -08002425def gen_new_function_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07002426 """
2427 Generate the new operator for all classes
2428
2429 @param out The file to which to write the functions
2430 """
2431
Rich Laneb604e332013-12-15 13:23:51 -08002432 gen_new_fn_body(cls, out)
2433 gen_init_fn_body(cls, out)
2434 if loxi_utils.class_is_message(cls):
2435 gen_from_message_fn_body(cls, out)
Rich Lanea06d0c32013-03-25 08:52:03 -07002436
Rich Lanea06d0c32013-03-25 08:52:03 -07002437"""
2438Document generation functions
2439
2440The main reason this is here is to generate documentation per
2441accessor that indicates the versions that support the interface.
2442"""
2443
2444
2445def gen_accessor_doc(out, name):
2446 """
2447 Generate documentation for each accessor function that indicates
2448 the versions supporting the accessor.
2449 """
2450
2451 common_top_matter(out, name)
2452
2453 out.write("/* DOCUMENTATION ONLY */\n")
2454
2455 for cls in of_g.standard_class_order:
2456 if cls in type_maps.inheritance_map:
2457 pass # Check this
2458
2459 out.write("""
2460/**
Andreas Wundsam53256162013-05-02 14:05:53 -07002461 * Structure for %(cls)s object. Get/set
Rich Lanea06d0c32013-03-25 08:52:03 -07002462 * accessors available in all versions unless noted otherwise
2463 *
2464""" % dict(cls=cls))
2465 if loxi_utils.class_is_list(cls):
2466 out.write("""\
2467 * @param first Function of type %(cls)s_first_f.
2468 * Setup a TBD class object to the first entry in the list
2469 * @param next Function of type %(cls)s_next_f.
2470 * Advance a TBD class object to the next entry in the list
2471 * @param append_bind Function of type %(cls)s_append_bind_f
2472 * Setup a TBD class object for append to the end of the current list
2473 * @param append Function of type @ref %(cls)s_append_f.
2474 * Copy an item to the end of a list
2475""" % dict(cls=cls))
2476
2477 for m_name in of_g.ordered_members[cls]:
2478 if m_name in of_g.skip_members:
2479 continue
2480 ver_type_map = field_ver_get(cls, m_name)
2481 (m_type, get_rv) = get_acc_rv(cls, m_name)
2482 if len(ver_type_map) == 3:
2483 # ver_string = "Available in all versions"
2484 ver_string = ""
2485 else:
2486 ver_string = "("
2487 for ver in sorted(ver_type_map):
2488 ver_string += " " + of_g.short_version_names[ver]
2489 ver_string += ")."
2490
2491 f_name = acc_name(cls, m_name)
2492 out.write("""\
2493 * @param %(m_name)s_get/set %(ver_string)s
2494 * Accessors for %(m_name)s, a variable of type %(m_type)s. Functions
2495 * are of type %(f_name)s_get_f and _set_f.
2496 *
2497""" % dict(f_name=f_name, m_name=m_name, ver_string=ver_string, m_type=m_type))
2498
2499 out.write("""\
2500 */
2501typedef struct %(cls)s_s %(cls)s_t;
2502""" % dict(cls=cls))
2503
2504 out.write("#endif /* _LOCI_DOC_H_ */\n")
2505
2506################################################################
2507#
2508# For fun, here are some unified, traditional C structure representation
2509#
2510################################################################
2511
2512def gen_cof_to_wire(out):
2513 pass
2514
2515def gen_wire_to_cof(out):
2516 pass
2517
2518def gen_cof_instance(out, cls):
2519 out.write("struct c%s_s {\n" % cls)
2520 for m in of_g.ordered_members[cls]:
2521 if m in of_g.skip_members:
2522 continue
2523 entry = of_g.unified[cls]["union"][m]
2524 cof_type = type_to_cof_type(entry["m_type"])
2525 out.write(" %-20s %s;\n" % (cof_type, m))
2526 out.write("};\n\n");
2527
2528def gen_cof_structs(out):
2529 """
2530 Generate non-version specific (common) representation of structures
2531
2532 @param out The file to which to write the functions
2533 """
2534
2535 out.write("\n/* Common, unified OpenFlow structure representations */\n")
2536 for cls in of_g.standard_class_order:
2537 if cls in type_maps.inheritance_map:
2538 continue
2539 gen_cof_instance(out, cls)
2540
2541################################################################
2542#
2543# Generate code samples for applications.
2544#
2545################################################################
2546
2547def gen_code_samples(out, name):
2548 out.write("""
2549#if 0 /* Do not compile in */
2550/**
2551 * @file %(name)s
2552 *
2553 * These are code samples for inclusion in other components
2554 */
2555
2556""" % dict(name=name))
2557
2558 gen_jump_table_template(out)
2559 # These are messages that a switch might expect.
2560 msg_list = ["of_echo_request",
2561 "of_hello",
2562 "of_packet_in",
2563 "of_packet_out",
2564 "of_port_mod",
2565 "of_port_stats_request",
2566 "of_queue_get_config_request",
2567 "of_queue_stats_request",
2568 "of_flow_add",
2569 "of_flow_modify",
2570 "of_flow_modify_strict",
2571 "of_flow_delete",
2572 "of_flow_delete_strict",
2573 "of_get_config_request",
2574 "of_flow_stats_request",
2575 "of_barrier_request",
2576 "of_echo_reply",
2577 "of_aggregate_stats_request",
2578 "of_desc_stats_request",
2579 "of_table_stats_request",
2580 "of_features_request",
2581 "of_table_mod",
2582 "of_set_config",
2583 "of_experimenter",
2584 "of_experimenter_stats_request",
2585 "of_group_desc_stats_request",
2586 "of_group_features_stats_request",
2587 "of_role_request"]
2588
2589 gen_message_handler_templates(out, msgs=msg_list)
2590
2591 out.write("""
2592#endif
2593""")
2594
2595def gen_jump_table_template(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002596 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002597 unhandled="unhandled_message"):
2598 """
2599 Generate a template for a jump table.
2600 @param out The file to which to write the functions
2601 """
2602 out.write("""
2603/*
2604 * Simple jump table definition for message handling
2605 */
2606typedef int (*msg_handler_f)(%(cxn_type)s cxn, of_object_t *obj);
2607typedef msg_handler_f msg_jump_table_t[OF_MESSAGE_OBJECT_COUNT];
2608
2609/* Jump table template for message objects */
2610extern msg_jump_table_t jump_table;
2611
2612/* C-code template */
2613msg_jump_table_t jump_table = {
2614 %(unhandled)s, /* OF_OBJECT; place holder for generic object */
2615""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2616 count = 0
2617 fn_name = unhandled
2618 for cls in of_g.ordered_messages:
2619 comma = ","
2620 count += 1
2621 if count == len(of_g.ordered_messages):
2622 comma = " "
2623 if not all_unhandled:
2624 fn_name = "%s_handler" % cls[3:]
2625 out.write(" %s%s /* %s */\n" % (fn_name, comma, enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002626
Rich Lanea06d0c32013-03-25 08:52:03 -07002627 out.write("};\n")
2628
2629def gen_message_switch_stmt_tmeplate(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002630 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002631 unhandled="unhandled_message"):
2632 out.write("""
2633/*
2634 * Simple switch statement for message handling
2635 */
2636
2637 switch (obj->object_id):
2638""")
2639 fn_name = unhandled
2640 for cls in of_g.ordered_messages:
2641 if not all_unhandled:
2642 fn_name = "%s_handler" % cls[3:]
2643 out.write("""
2644 case %(enum)s:
2645 rv = %(fn_name)s(cxn, obj);
2646 break;
2647""" % dict(fn_name=fn_name, cls=cls, enum=enum_name(cls)))
2648 out.write("""
2649 default:
2650 rv = LS_ERROR_PARAM;
2651 break;
2652 }
2653
2654 TRACE("Handled msg %p with rv %d (%s)", obj, rv, ls_error_strings[rv]);
2655
2656 return rv;
2657""")
2658
2659
2660def gen_message_handler_templates(out=sys.stdout, cxn_type="ls_cxn_handle_t",
2661 unhandled="unhandled_message", msgs=None):
2662 gen_jump_table_template(out, False, cxn_type)
2663 out.write("""
2664/**
2665 * Function for unhandled message
2666 */
2667static int
2668unhandled_message(%(cxn_type)s cxn, of_object_t *obj)
2669{
2670 (void)cxn;
2671 (void)obj;
2672 TRACE("Unhandled message %%p. Object id %%d", obj, obj->object_id);
2673
2674 return LS_ERROR_UNAVAIL;
2675}
2676""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2677
2678 if not msgs:
2679 msgs = of_g.ordered_messages
2680 for cls in msgs:
2681 out.write("""
2682/**
2683 * Handle a %(s_cls)s message
2684 * @param cxn Connection handler for the owning connection
2685 * @param _obj Generic type object for the message to be coerced
2686 * @returns Error code
2687 */
2688
2689static int
2690%(s_cls)s_handler(%(cxn_type)s cxn, of_object_t *_obj)
2691{
2692 %(cls)s_t *obj;
2693
2694 TRACE("Handling %(cls)s message: %%p.", obj);
2695 obj = (%(cls)s_t *)_obj;
2696
2697 /* Handle object of type %(cls)s_t */
2698
2699 return LS_ERROR_NONE;
2700}
2701""" % dict(s_cls=cls[3:], cls=cls, cxn_type=cxn_type))
2702 gen_message_switch_stmt_tmeplate(out, False, cxn_type)