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