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