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