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