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