blob: d1e4348f37062ebbaf04a22638f8b010a301a3e8 [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)
Rich Lanea06d0c32013-03-25 08:52:03 -0700434 out.write("""
435/****************************************************************
436 *
437 * Declarations of maps between on-the-wire type values and LOCI identifiers
438 *
439 ****************************************************************/
440""")
441 c_type_maps.gen_type_maps_header(out)
442 c_type_maps.gen_type_data_header(out)
443 c_match.gen_declarations(out)
444 # @fixme Move debug stuff to own fn
445 out.write("""
446/**
447 * Macro to check consistency of length for top level objects
448 *
449 * If the object has no parent then its length should match the
450 * underlying wire buffer's current bytes.
451 */
452#define OF_LENGTH_CHECK_ASSERT(obj) \\
453 ASSERT(((obj)->parent != NULL) || \\
454 ((obj)->wire_object.wbuf == NULL) || \\
455 (WBUF_CURRENT_BYTES((obj)->wire_object.wbuf) == (obj)->length))
456
457#define OF_DEBUG_DUMP
458#if defined(OF_DEBUG_DUMP)
459extern void dump_match(of_match_t *match);
460#endif /* OF_DEBUG_DUMP */
461""")
462
463 out.write("\n#endif /* Top header file */\n")
464
465def match_c_gen(out, name):
466 """
467 Generate code for
468 @param out The file handle to write to
469 @param name The name of the file
470 """
471 c_match.match_c_top_matter(out, name)
472 c_match.gen_match_conversions(out)
473 c_match.gen_serialize(out)
474 c_match.gen_deserialize(out)
475
Rich Lanea06d0c32013-03-25 08:52:03 -0700476def type_data_c_gen(out, name):
477 common_top_matter(out, name)
478 c_type_maps.gen_type_maps(out)
479 c_type_maps.gen_length_array(out)
Rich Lanef70be942013-07-18 13:33:14 -0700480 c_type_maps.gen_extra_length_array(out)
Rich Lanea06d0c32013-03-25 08:52:03 -0700481
482################################################################
483# Top Matter
484################################################################
485
486def common_top_matter(out, name):
487 loxi_utils.gen_c_copy_license(out)
488 out.write("""\
Rich Laned983aa52013-06-13 11:48:37 -0700489
Rich Lanea06d0c32013-03-25 08:52:03 -0700490/****************************************************************
491 * File: %s
492 *
493 * DO NOT EDIT
494 *
495 * This file is automatically generated
496 *
497 ****************************************************************/
498
499""" % name)
500
501 if name[-2:] == ".h":
502 out.write("""
503#if !defined(%(h)s)
504#define %(h)s
505
506""" % dict(h=h_file_to_define(name)))
507
508def base_h_content(out):
509 """
510 Generate base header file content
511
512 @param out The output file object
513 """
514
515 # @fixme Supported version should be generated based on input to LoxiGen
516
517 out.write("""
518/*
519 * Base OpenFlow definitions. These depend only on standard C headers
520 */
521#include <string.h>
522#include <stdint.h>
523
524/* g++ requires this to pick up PRI, etc.
525 * See http://gcc.gnu.org/ml/gcc-help/2006-10/msg00223.html
526 */
527#if !defined(__STDC_FORMAT_MACROS)
528#define __STDC_FORMAT_MACROS
529#endif
530#include <inttypes.h>
531
532#include <stdlib.h>
533#include <assert.h>
534#include <loci/loci_idents.h>
535
536/**
537 * Macro to enable debugging for LOCI.
538 *
539 * This enables debug output to stdout.
540 */
541#define OF_DEBUG_ENABLE
542
543#if defined(OF_DEBUG_ENABLE)
544#include <stdio.h> /* Currently for debugging */
545#define FIXME(str) do { \\
546 fprintf(stderr, "%s\\n", str); \\
547 exit(1); \\
548 } while (0)
549#define debug printf
550#else
551#define FIXME(str)
552#define debug(str, ...)
553#endif /* OF_DEBUG_ENABLE */
554
555/**
556 * The type of a function used by the LOCI dump/show functions to
557 * output text. Essentially the same signature as fprintf. May
558 * be called many times per invocation of e.g. of_object_show().
559 */
560typedef int (*loci_writer_f)(void *cookie, const char *fmt, ...);
561
562/**
563 * Check if a version is supported
564 */
565#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_3)
566
567""")
568 gen_version_enum(out)
569 out.write("\n")
570
571 # for c_name in of_g.ofp_constants:
572 # val = str(of_g.ofp_constants[c_name])
573 # out.write("#define %s %s\n" % (c_name, val))
574 # out.write("\n")
575
576 out.write("""
577typedef enum of_error_codes_e {
578 OF_ERROR_NONE = 0,
579 OF_ERROR_RESOURCE = -1, /* Could not allocate space */
580 OF_ERROR_PARAM = -2, /* Bad parameter */
581 OF_ERROR_VERSION = -3, /* Version not supported */
582 OF_ERROR_RANGE = -4, /* End of list indication */
583 OF_ERROR_COMPAT = -5, /* Incompatible assignment */
584 OF_ERROR_PARSE = -6, /* Error in parsing data */
585 OF_ERROR_INIT = -7, /* Uninitialized data */
586 OF_ERROR_UNKNOWN = -8 /* Unknown error */
587} of_error_codes_t;
588
589#define OF_ERROR_STRINGS "none", \\
590 "resource", \\
591 "parameter", \\
592 "version", \\
593 "range", \\
594 "incompatible", \\
595 "parse", \\
596 "init", \\
597 "unknown"
598
Rich Laneb157b0f2013-03-27 13:55:28 -0700599extern const char *const of_error_strings[];
Rich Lanea06d0c32013-03-25 08:52:03 -0700600
Rich Lane53757732013-02-23 17:00:10 -0800601#ifndef NDEBUG
Rich Lanea06d0c32013-03-25 08:52:03 -0700602/* #define ASSERT(val) assert(val) */
603#define FORCE_FAULT *(volatile int *)0 = 1
604#define ASSERT(val) if (!(val)) \\
605 fprintf(stderr, "\\nASSERT %s. %s:%d\\n", #val, __FILE__, __LINE__), \\
606 FORCE_FAULT
Rich Lane53757732013-02-23 17:00:10 -0800607#else
608#define ASSERT(val)
609#endif
Rich Lanea06d0c32013-03-25 08:52:03 -0700610
611/*
612 * Some LOCI object accessors can fail, and it's easy to forget to check.
613 * On certain compilers we can trigger a warning if the error code
614 * is ignored.
615 */
616#ifndef DISABLE_WARN_UNUSED_RESULT
617#ifdef __GNUC__
618#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
619#else
620#define WARN_UNUSED_RESULT
621#endif
622#else
623#define WARN_UNUSED_RESULT
624#endif
625
626typedef union of_generic_u of_generic_t;
627typedef struct of_object_s of_object_t;
628
629/* Define ipv4 address as uint32 */
630typedef uint32_t of_ipv4_t;
631
632/* Table ID is the OF standard uint8 */
633typedef uint8_t of_table_id_t;
634
635#define OF_MAC_ADDR_BYTES 6
636typedef struct of_mac_addr_s {
637 uint8_t addr[OF_MAC_ADDR_BYTES];
638} of_mac_addr_t;
639
640#define OF_IPV6_BYTES 16
641typedef struct of_ipv6_s {
642 uint8_t addr[OF_IPV6_BYTES];
643} of_ipv6_t;
644
645extern const of_mac_addr_t of_mac_addr_all_ones;
646extern const of_mac_addr_t of_mac_addr_all_zeros;
647
648extern const of_ipv6_t of_ipv6_all_ones;
649extern const of_ipv6_t of_ipv6_all_zeros;
650
651/**
652 * Generic zero and all-ones values of size 16 bytes.
653 *
654 * IPv6 is longest data type we worry about for comparisons
655 */
656#define of_all_zero_value of_ipv6_all_zeros
657#define of_all_ones_value of_ipv6_all_ones
658
659/**
660 * Non-zero/all ones check for arbitrary type of size <= 16 bytes
661 */
662#define OF_VARIABLE_IS_NON_ZERO(_ptr) \\
663 (MEMCMP(&of_all_zero_value, (_ptr), sizeof(*(_ptr))))
664#define OF_VARIABLE_IS_ALL_ONES(_ptr) \\
665 (!MEMCMP(&of_all_ones_value, (_ptr), sizeof(*(_ptr))))
666
667/* The octets object is a struct holding pointer and length */
668typedef struct of_octets_s {
669 uint8_t *data;
670 int bytes;
671} of_octets_t;
672
673/* Macro to convert an octet object to a pointer; currently trivial */
674#define OF_OCTETS_POINTER_GET(octet_ptr) ((octet_ptr)->data)
675#define OF_OCTETS_POINTER_SET(octet_ptr, ptr) (octet_ptr)->data = (ptr)
676#define OF_OCTETS_BYTES_GET(octet_ptr) ((octet_ptr)->bytes)
677#define OF_OCTETS_BYTES_SET(octet_ptr, bytes) (octet_ptr)->bytes = (bytes)
678
679/* Currently these are categorized as scalars */
680typedef char of_port_name_t[OF_MAX_PORT_NAME_LEN];
681typedef char of_table_name_t[OF_MAX_TABLE_NAME_LEN];
682typedef char of_desc_str_t[OF_DESC_STR_LEN];
683typedef char of_serial_num_t[OF_SERIAL_NUM_LEN];
684
Rich Lane3b2fd832013-09-24 13:44:08 -0700685typedef struct of_bitmap_128_s {
686 uint64_t hi;
687 uint64_t lo;
688} of_bitmap_128_t;
689
Rich Lanea06d0c32013-03-25 08:52:03 -0700690/* These are types which change across versions. */
691typedef uint32_t of_port_no_t;
692typedef uint16_t of_fm_cmd_t;
693typedef uint64_t of_wc_bmap_t;
694typedef uint64_t of_match_bmap_t;
695
696#define MEMMOVE(dest, src, bytes) memmove(dest, src, bytes)
697#define MEMSET(dest, val, bytes) memset(dest, val, bytes)
698#define MEMCPY(dest, src, bytes) memcpy(dest, src, bytes)
699#define MEMCMP(a, b, bytes) memcmp(a, b, bytes)
700#define MALLOC(bytes) malloc(bytes)
701#define FREE(ptr) free(ptr)
702
703/** Try an operation and return on failure. */
704#define OF_TRY(op) do { \\
705 int _rv; \\
706 if ((_rv = (op)) < 0) { \\
707 LOCI_LOG_ERROR("ERROR %d at %s:%d\\n", _rv, __FILE__, __LINE__); \\
708 return _rv; \\
709 } \\
710 } while (0)
711
712/* The extent of an OF match object is determined by its length field, but
713 * aligned to 8 bytes
714 */
715
716#define OF_MATCH_BYTES(length) (((length) + 7) & 0xfff8)
717
718#if __BYTE_ORDER == __BIG_ENDIAN
719#define U16_NTOH(val) (val)
720#define U32_NTOH(val) (val)
721#define U64_NTOH(val) (val)
722#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
723#define U16_HTON(val) (val)
724#define U32_HTON(val) (val)
725#define U64_HTON(val) (val)
726#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
727#else /* Little Endian */
728#define U16_NTOH(val) (((val) >> 8) | ((val) << 8))
729#define U32_NTOH(val) ((((val) & 0xff000000) >> 24) | \\
730 (((val) & 0x00ff0000) >> 8) | \\
731 (((val) & 0x0000ff00) << 8) | \\
732 (((val) & 0x000000ff) << 24))
733#define U64_NTOH(val) ((((val) & 0xff00000000000000LL) >> 56) | \\
734 (((val) & 0x00ff000000000000LL) >> 40) | \\
735 (((val) & 0x0000ff0000000000LL) >> 24) | \\
736 (((val) & 0x000000ff00000000LL) >> 8) | \\
737 (((val) & 0x00000000ff000000LL) << 8) | \\
738 (((val) & 0x0000000000ff0000LL) << 24) | \\
739 (((val) & 0x000000000000ff00LL) << 40) | \\
740 (((val) & 0x00000000000000ffLL) << 56))
741#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
742#define U16_HTON(val) U16_NTOH(val)
743#define U32_HTON(val) U32_NTOH(val)
744#define U64_HTON(val) U64_NTOH(val)
745#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
746#endif
747
748/****************************************************************
749 *
750 * The following are internal definitions used by the automatically
751 * generated code. Users should not reference these definitions
752 * as they may change between versions of this code
753 *
754 ****************************************************************/
755
756#define OF_MESSAGE_IN_MATCH_POINTER(obj) \\
757 (WIRE_BUF_POINTER(&((obj)->wire_buffer), OF_MESSAGE_IN_MATCH_OFFSET))
758#define OF_MESSAGE_IN_MATCH_LEN(ptr) BUF_U16_GET(&ptr[2])
759#define OF_MESSAGE_IN_DATA_OFFSET(obj) \\
760 (FIXED_LEN + OF_MESSAGE_IN_MATCH_LEN(OF_MESSAGE_IN_MATCH_POINTER(obj)) + 2)
761
762#define OF_MESSAGE_OUT_DATA_OFFSET(obj) \\
763 (FIXED_LEN + of_message_out_actions_len_get(obj))
764
765""")
766
767def external_h_top_matter(out, name):
768 """
769 Generate top matter for external header file
770
771 @param name The name of the output file
772 @param out The output file object
773 """
774 common_top_matter(out, name)
775 out.write("""
776#include <loci/loci_base.h>
777#include <loci/of_message.h>
778#include <loci/of_match.h>
779#include <loci/of_object.h>
Rich Lanedef2e512013-12-15 15:54:02 -0800780#include <loci/loci_classes.h>
Rich Lanea06d0c32013-03-25 08:52:03 -0700781
782/****************************************************************
783 *
784 * This file is divided into the following sections.
785 *
786 * A few object specific macros
787 * Class typedefs (no struct definitions)
788 * Per-data type accessor function typedefs
789 * Per-class new/delete function typedefs
790 * Per-class static delete functions
791 * Per-class, per-member accessor declarations
792 * Per-class structure definitions
793 * Generic union (inheritance) definitions
794 * Pointer set function declarations
795 * Some special case macros
796 *
797 ****************************************************************/
798""")
799
Rich Lanea06d0c32013-03-25 08:52:03 -0700800################################################################
801#
802################################################################
803
804def gen_version_enum(out):
805 """
806 Generate the enumerated type for versions in LoxiGen
807 @param out The file object to which to write the decs
808
809 This just uses the wire versions for now
810 """
811 out.write("""
812/**
813 * Enumeration of OpenFlow versions
814 *
815 * The wire protocol numbers are currently used for values of the corresponding
816 * version identifiers.
817 */
818typedef enum of_version_e {
819 OF_VERSION_UNKNOWN = 0,
820""")
821
822 is_first = True
823 max = 0
824 for v in of_g.wire_ver_map:
825 if is_first:
826 is_first = False
827 else:
828 out.write(",\n")
829 if v > max:
830 max = v
831 out.write(" %s = %d" % (of_g.wire_ver_map[v], v))
832
833 out.write("""
834} of_version_t;
835
836/**
837 * @brief Use this when declaring arrays indexed by wire version
838 */
839#define OF_VERSION_ARRAY_MAX %d
840""" % (max + 1))
Andreas Wundsam53256162013-05-02 14:05:53 -0700841
Rich Lanea06d0c32013-03-25 08:52:03 -0700842def gen_object_enum(out):
843 """
844 Generate the enumerated type for object identification in LoxiGen
845 @param out The file object to which to write the decs
846 """
847 out.write("""
848
849/**
850 * Enumeration of OpenFlow objects
851 *
852 * We enumerate the OpenFlow objects used internally. Note that some
853 * message types are determined both by an outer type (message type like
854 * stats_request) and an inner type (port stats). These are different
855 * messages in ofC.
856 *
857 * These values are for internal use only. They will change with
858 * different versions of ofC.
859 */
860
861typedef enum of_object_id_e {
862 /* Root object type */
863 OF_OBJECT_INVALID = -1, /* "invalid" return value for mappings */
864 OF_OBJECT = 0, /* Generic, untyped object */
865
866 /* OpenFlow message objects */
867""")
868 last = 0
869 msg_count = 0
870 for cls in of_g.ordered_messages:
871 out.write(" %s = %d,\n" % (enum_name(cls),
872 of_g.unified[cls]["object_id"]))
873 msg_count = of_g.unified[cls]["object_id"] + 1
874
875 out.write("\n /* Non-message objects */\n")
876 for cls in of_g.ordered_non_messages:
877 out.write(" %s = %d,\n" % (enum_name(cls),
878 of_g.unified[cls]["object_id"]))
879 last = of_g.unified[cls]["object_id"]
880 out.write("\n /* List objects */\n")
881 for cls in of_g.ordered_list_objects:
882 out.write(" %s = %d,\n" % (enum_name(cls),
883 of_g.unified[cls]["object_id"]))
884 last = of_g.unified[cls]["object_id"]
885
886 out.write("\n /* Generic stats request/reply types; pseudo objects */\n")
887 for cls in of_g.ordered_pseudo_objects:
888 out.write(" %s = %d,\n" % (enum_name(cls),
889 of_g.unified[cls]["object_id"]))
890 last = of_g.unified[cls]["object_id"]
891
892 out.write("""
893 OF_OBJECT_COUNT = %d
894} of_object_id_t;
895
Rich Laneb157b0f2013-03-27 13:55:28 -0700896extern const char *const of_object_id_str[];
Rich Lanea06d0c32013-03-25 08:52:03 -0700897
898#define OF_MESSAGE_OBJECT_COUNT %d
899""" % ((last + 1), msg_count))
900
901 # Generate object type range checking for inheritance classes
902
903 # @fixme These should be determined algorithmicly
904 out.write("""
905/*
906 * Macros to check if an object ID is within an inheritance class range
907 */
908""")
909 # Alphabetical order for 'last'
910 last_ids = dict(of_action="OF_ACTION_STRIP_VLAN",
911 of_oxm="OF_OXM_VLAN_VID_MASKED",
912 of_instruction="OF_INSTRUCTION_WRITE_METADATA",
913 of_queue_prop="OF_QUEUE_PROP_MIN_RATE",
914 of_table_feature_prop="OF_TABLE_FEATURE_PROP_WRITE_SETFIELD_MISS",
915 # @FIXME add meter_band ?
916 )
917 for cls, last in last_ids.items():
918 out.write("""
919#define %(enum)s_FIRST_ID (%(enum)s + 1)
920#define %(enum)s_LAST_ID %(last)s
921#define %(enum)s_VALID_ID(id) \\
922 ((id) >= %(enum)s_FIRST_ID && \\
923 (id) <= %(enum)s_LAST_ID)
924""" % dict(enum=enum_name(cls), last=last))
925 out.write("""
926/**
927 * Function to check a wire ID
928 * @param object_id The ID to check
929 * @param base_object_id The inheritance parent, if applicable
930 * @returns boolean: If base_object_id is an inheritance class, check if
931 * object_id is valid as a subclass. Otherwise return 1.
932 *
933 * Note: Could check that object_id == base_object_id in the
934 * second case.
935 */
936static inline int
937of_wire_id_valid(int object_id, int base_object_id) {
938 switch (base_object_id) {
939 case OF_ACTION:
940 return OF_ACTION_VALID_ID(object_id);
941 case OF_OXM:
942 return OF_OXM_VALID_ID(object_id);
943 case OF_QUEUE_PROP:
944 return OF_QUEUE_PROP_VALID_ID(object_id);
945 case OF_TABLE_FEATURE_PROP:
946 return OF_TABLE_FEATURE_PROP_VALID_ID(object_id);
947 case OF_INSTRUCTION:
948 return OF_INSTRUCTION_VALID_ID(object_id);
949 default:
950 break;
951 }
952 return 1;
953}
954""")
955
Rich Lanea06d0c32013-03-25 08:52:03 -0700956################################################################
957#
958# Internal Utility Functions
959#
960################################################################
961
962
963def acc_name(cls, m_name):
964 """
965 Generate the root name of an accessor function for typedef
966 @param cls The class name
967 @param m_name The member name
968 """
969 (m_type, get_rv) = get_acc_rv(cls, m_name)
970 return "%s_%s" % (cls, m_type)
971
972def get_acc_rv(cls, m_name):
973 """
974 Determine the data type and return type for a get accessor.
975
976 The return type may be "void" or it may be the accessor type
977 depending on the system configuration and on the data type.
978
979 @param cls The class name
980 @param m_name The member name
981 @return A pair (m_type, rv) where m_type is the unified type of the
982 member and rv is the get_accessor return type
983 """
984 member = of_g.unified[cls]["union"][m_name]
985 m_type = member["m_type"]
986 rv = "int"
987 if member_returns_val(cls, m_name):
988 rv = m_type
989 if m_type[-2:] == "_t":
990 m_type = m_type[:-2]
991
992 return (m_type, rv)
993
994def param_list(cls, m_name, a_type):
995 """
996 Generate the parameter list (no parens) for an a_type accessor
997 @param cls The class name
998 @param m_name The member name
999 @param a_type One of "set" or "get" or TBD
1000 """
1001 member = of_g.unified[cls]["union"][m_name]
1002 m_type = member["m_type"]
1003 params = ["%s_t *obj" % cls]
1004 if a_type == "set":
1005 if loxi_utils.type_is_scalar(m_type):
1006 params.append("%s %s" % (m_type, m_name))
1007 else:
1008 params.append("%s *%s" % (m_type, m_name))
1009 elif a_type in ["get", "bind"]:
1010 params.append("%s *%s" % (m_type, m_name))
1011 else:
1012 debug("Class %s, name %s Bad param list a_type: %s" %
1013 (cls, m_name, a_type))
1014 sys.exit(1)
1015 return params
1016
1017def typed_function_base(cls, m_name):
1018 """
1019 Generate the core name for accessors based on the type
1020 @param cls The class name
1021 @param m_name The member name
1022 """
1023 (m_type, get_rv) = get_acc_rv(cls, m_name)
1024 return "%s_%s" % (cls, m_type)
1025
1026def member_function_base(cls, m_name):
1027 """
1028 Generate the core name for accessors based on the member name
1029 @param cls The class name
1030 @param m_name The member name
1031 """
1032 return "%s_%s" % (cls, m_name)
1033
1034def field_ver_get(cls, m_name):
1035 """
1036 Generate a dict, indexed by wire version, giving a pair (type, offset)
1037
1038 @param cls The class name
1039 @param m_name The name of the class member
1040
1041 If offset is not known for m_name, the type
1042 A dict is used for more convenient indexing.
1043 """
1044 result = {}
1045
1046 for ver in of_g.unified[cls]:
1047 if type(ver) == type(0): # It's a version
1048 if "use_version" in of_g.unified[cls][ver]: # deref version ref
1049 ref_ver = of_g.unified[cls][ver]["use_version"]
1050 members = of_g.unified[cls][ref_ver]["members"]
1051 else:
1052 members = of_g.unified[cls][ver]["members"]
1053 idx = loxi_utils.member_to_index(m_name, members)
1054 if (idx < 0):
1055 continue # Member not in this version
1056 m_type = members[idx]["m_type"]
1057 offset = members[idx]["offset"]
1058
1059 # If m_type is mixed, get wire version type from global data
1060 if m_type in of_g.of_mixed_types and \
1061 ver in of_g.of_mixed_types[m_type]:
1062 m_type = of_g.of_mixed_types[m_type][ver]
1063
1064 # add version to result list
1065 result[ver] = (m_type, offset)
1066
1067 return result
1068
1069def v3_match_offset_get(cls):
1070 """
Andreas Wundsam53256162013-05-02 14:05:53 -07001071 Return the offset of an OF 1.2 match in an object if it has such;
Rich Lanea06d0c32013-03-25 08:52:03 -07001072 otherwise return -1
1073 """
1074 result = field_ver_get(cls, "match")
1075 if of_g.VERSION_1_2 in result:
1076 if result[of_g.VERSION_1_2][0] == "of_match_v3_t":
1077 return result[of_g.VERSION_1_2][1]
1078 return -1
1079
1080################################################################
1081#
1082# OpenFlow Object Definitions
1083#
1084################################################################
1085
1086
1087def gen_of_object_defs(out):
1088 """
1089 Generate low level of_object core operations
1090 @param out The file for output, already open
1091 """
1092
1093def gen_generics(out):
1094 for (cls, subclasses) in type_maps.inheritance_map.items():
1095 out.write("""
1096/**
1097 * Inheritance super class for %(cls)s
1098 *
1099 * This class is the union of %(cls)s classes. You can refer
1100 * to it untyped by refering to the member 'header' whose structure
1101 * is common across all sub-classes.
1102 */
1103
1104union %(cls)s_u {
1105 %(cls)s_header_t header; /* Generic instance */
1106""" % dict(cls=cls))
1107 for subcls in sorted(subclasses):
1108 out.write(" %s_%s_t %s;\n" % (cls, subcls, subcls))
1109 out.write("};\n")
1110
1111def gen_struct_typedefs(out):
1112 """
1113 Generate typedefs for all struct objects
1114 @param out The file for output, already open
1115 """
1116
1117 out.write("\n/* LOCI inheritance parent typedefs */\n")
1118 for cls in type_maps.inheritance_map:
1119 out.write("typedef union %(cls)s_u %(cls)s_t;\n" % dict(cls=cls))
1120 out.write("\n/* LOCI object typedefs */\n")
1121 for cls in of_g.standard_class_order:
1122 if cls in type_maps.inheritance_map:
1123 continue
Rich Lane85767872013-12-15 16:24:42 -08001124 template = "typedef of_object_t %(cls)s_t;\n"
1125 out.write(template % dict(cls=cls))
Rich Lanea06d0c32013-03-25 08:52:03 -07001126
1127 out.write("""
1128/****************************************************************
1129 *
1130 * Additional of_object defines
1131 * These are needed for some static inline ops, so we put them here.
1132 *
1133 ****************************************************************/
1134
1135/* Delete an OpenFlow object without reference to its type */
1136extern void of_object_delete(of_object_t *obj);
1137
1138""")
1139
1140def gen_generic_union(out):
1141 """
1142 Generate the generic union object composing all LOCI objects
1143
1144 @param out The file to which to write the decs
1145 """
1146 out.write("""
1147/**
1148 * The common LOCI object is a union of all possible objects.
1149 */
1150union of_generic_u {
1151 of_object_t object; /* Common base class with fundamental accessors */
1152
1153 /* Message objects */
1154""")
1155 for cls in of_g.ordered_messages:
1156 out.write(" %s_t %s;\n" % (cls, cls))
1157 out.write("\n /* Non-message composite objects */\n")
1158 for cls in of_g.ordered_non_messages:
1159 if cls in type_maps.inheritance_map:
1160 continue
1161 out.write(" %s_t %s;\n" % (cls, cls))
1162 out.write("\n /* List objects */\n")
1163 for cls in of_g.ordered_list_objects:
1164 out.write(" %s_t %s;\n" % (cls, cls))
1165 out.write("};\n")
1166
Rich Lanea06d0c32013-03-25 08:52:03 -07001167def gen_flow_add_setup_function_declarations(out):
1168 """
1169 Add the declarations for functions that can be initialized
1170 by a flow add. These are defined external to LOXI.
1171 """
1172
1173 out.write("""
1174/****************************************************************
1175 * Functions for objects that can be initialized by a flow add message.
1176 * These are defined in a non-autogenerated file
1177 ****************************************************************/
1178
1179/**
1180 * @brief Set up a flow removed message from the original add
1181 * @param obj The flow removed message being updated
1182 * @param flow_add The flow_add message to use
1183 *
1184 * Initialize the following fields of obj to be identical
1185 * to what was originally on the wire from the flow_add object:
1186 * match
1187 * cookie
1188 * priority
1189 * idle_timeout
1190 * hard_timeout
1191 *
1192 */
1193
1194extern int
1195of_flow_removed_setup_from_flow_add(of_flow_removed_t *obj,
1196 of_flow_add_t *flow_add);
1197
1198
1199/**
1200 * @brief Set up the packet in match structure from the original add
1201 * @param obj The packet in message being updated
1202 * @param flow_add The flow_add message to use
1203 * @returns Indigo error code. Does not return a version error if
1204 * the version does not require initializing obj.
1205 *
1206 * Initialize the match member of obj to be identical to what was originally
1207 * on the wire from the flow_add object. If applicable, the table ID is also
1208 * initialized from the flow_add object.
1209 *
1210 * This API applies to 1.2 and later only.
1211 */
1212
1213extern int
1214of_packet_in_setup_from_flow_add(of_packet_in_t *obj,
1215 of_flow_add_t *flow_add);
1216
1217
1218/**
1219 * @brief Set up the flow stats entry from the original add
1220 * @param obj The packet in message being updated
1221 * @param flow_add The flow_add message to use
1222 * @param effects Optional actions or instructions; see below.
1223 *
1224 * Initialize the following fields of obj to be identical
1225 * to what was originally on the wire from the flow_add object:
1226 * match
1227 * actions/instructions (effects)
1228 * cookie
1229 * priority
1230 * idle_timeout
1231 * hard_timeout
1232 *
Andreas Wundsam53256162013-05-02 14:05:53 -07001233 * Note that the actions/instructions of a flow may be modified by a
Rich Lanea06d0c32013-03-25 08:52:03 -07001234 * subsequent flow modify message. To facilitate implementations,
1235 * the "effects" parameter is provided. If effects is NULL, the
1236 * actions/instructions are taken from the flow_add message.
1237 * Otherwise, effects is coerced to the proper type (actions or
1238 * instructions) and used to init obj.
1239 */
1240
1241extern int
1242of_flow_stats_entry_setup_from_flow_add(of_flow_stats_entry_t *obj,
1243 of_flow_add_t *flow_add,
1244 of_object_t *effects);
1245""")
1246
Rich Lanea06d0c32013-03-25 08:52:03 -07001247################################################################
1248#
1249# List accessor code generation
1250#
1251# Currently these all implement copy on read semantics
1252#
1253################################################################
1254
1255def init_call(e_type, obj, ver, length, cw):
1256 """
1257 Generate the init call given the strings for params
1258 """
1259 hdr = "" # If inheritance type, coerce to hdr object
1260 obj_name = obj
1261 if e_type in type_maps.inheritance_map:
1262 hdr = "_header"
1263 obj_name = "(%s_header_t *)" % e_type + obj
1264
1265 return """\
1266%(e_type)s%(hdr)s_init(%(obj_name)s,
1267 %(ver)s, %(length)s, %(cw)s)\
1268""" % dict(e_type=e_type, hdr=hdr, obj_name=obj_name, ver=ver,
1269 length=length, cw=cw)
1270
1271def gen_list_first(out, cls, e_type):
1272 """
1273 Generate the body of a list_first operation
1274 @param cls The class name for which code is being generated
1275 @param e_type The element type of the list
1276 @param out The file to which to write
1277 """
1278 i_call = init_call(e_type, "obj", "list->version", "0", "1")
1279 if e_type in type_maps.inheritance_map:
1280 len_str = "obj->header.length"
1281 else:
1282 len_str = "obj->length"
1283
1284 out.write("""
1285/**
1286 * Associate an iterator with a list
1287 * @param list The list to iterate over
1288 * @param obj The list entry iteration pointer
1289 * @return OF_ERROR_RANGE if the list is empty (end of list)
1290 *
1291 * The obj instance is completely initialized. The caller is responsible
1292 * for cleaning up any wire buffers associated with obj before this call
1293 */
1294
1295int
1296%(cls)s_first(%(cls)s_t *list,
1297 %(e_type)s_t *obj)
1298{
1299 int rv;
1300
1301 %(i_call)s;
1302 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1303 return rv;
1304 }
1305""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1306
1307 # Special case flow_stats_entry lists
1308
1309 out.write("""
1310 of_object_wire_init((of_object_t *) obj, %(u_type)s,
1311 list->length);
1312 if (%(len_str)s == 0) {
1313 return OF_ERROR_PARSE;
1314 }
1315
1316 return rv;
1317}
1318""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1319
1320
1321def gen_bind(out, cls, m_name, m_type):
1322 """
1323 Generate the body of a bind function
1324 @param out The file to which to write
1325 @param cls The class name for which code is being generated
1326 @param m_name The name of the data member
1327 @param m_type The type of the data member
1328 """
1329
1330 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1331
1332 i_call = init_call(e_type, "child", "parent->version", "0", "1")
1333
1334 out.write("""
1335/**
1336 * Bind the child object to the parent object for read processing
1337 * @param parent The parent object
1338 * @param child The child object
1339 *
1340 * The child obj instance is completely initialized.
1341 */
1342
1343int
1344%(cls)s_%(m_name)_bind(%(cls)s_t *parent,
1345 %(e_type)s_t *child)
1346{
1347 int rv;
1348
1349 %(i_call)s;
1350
1351 /* Derive offset and length of child in parent */
Andreas Wundsam53256162013-05-02 14:05:53 -07001352 OF_TRY(of_object_child_attach(parent, child,
Rich Lanea06d0c32013-03-25 08:52:03 -07001353 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1354 return rv;
1355 }
1356""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1357
1358 # Special case flow_stats_entry lists
1359
1360 out.write("""
1361 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1362 list->length);
1363 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1364 return OF_ERROR_PARSE;
1365 }
1366
1367 return rv;
1368}
1369""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1370
1371
1372def gen_list_next(out, cls, e_type):
1373 """
1374 Generate the body of a list_next operation
1375 @param cls The class name for which code is being generated
1376 @param e_type The element type of the list
1377 @param out The file to which to write
1378 """
1379
1380 if e_type in type_maps.inheritance_map:
1381 len_str = "obj->header.length"
1382 else:
1383 len_str = "obj->length"
Andreas Wundsam53256162013-05-02 14:05:53 -07001384
Rich Lanea06d0c32013-03-25 08:52:03 -07001385 out.write("""
1386/**
1387 * Advance an iterator to the next element in a list
1388 * @param list The list being iterated
1389 * @param obj The list entry iteration pointer
1390 * @return OF_ERROR_RANGE if already at the last entry on the list
1391 *
1392 */
1393
1394int
1395%(cls)s_next(%(cls)s_t *list,
1396 %(e_type)s_t *obj)
1397{
1398 int rv;
1399
1400 if ((rv = of_list_next((of_object_t *)list, (of_object_t *)obj)) < 0) {
1401 return rv;
1402 }
1403
1404 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1405 list->length);
1406
1407 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1408 return OF_ERROR_PARSE;
1409 }
1410
1411 return rv;
1412}
1413""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1414
1415def gen_list_append(out, cls, e_type):
1416 """
1417 Generate the body of a list append functions
1418 @param cls The class name for which code is being generated
1419 @param e_type The element type of the list
1420 @param out The file to which to write
1421 """
1422
1423 out.write("""
1424/**
1425 * Set up to append an object of type %(e_type)s to an %(cls)s.
1426 * @param list The list that is prepared for append
1427 * @param obj Pointer to object to hold data to append
1428 *
1429 * The obj instance is completely initialized. The caller is responsible
1430 * for cleaning up any wire buffers associated with obj before this call.
1431 *
1432 * See the generic documentation for of_list_append_bind.
1433 */
1434
1435int
1436%(cls)s_append_bind(%(cls)s_t *list,
1437 %(e_type)s_t *obj)
1438{
1439 return of_list_append_bind((of_object_t *)list, (of_object_t *)obj);
1440}
1441
1442/**
1443 * Append an item to a %(cls)s list.
1444 *
1445 * This copies data from item and leaves item untouched.
1446 *
1447 * See the generic documentation for of_list_append.
1448 */
1449
1450int
1451%(cls)s_append(%(cls)s_t *list,
1452 %(e_type)s_t *item)
1453{
1454 return of_list_append((of_object_t *)list, (of_object_t *)item);
1455}
1456
1457""" % dict(cls=cls, e_type=e_type))
1458
1459def gen_list_accessors(out, cls):
1460 e_type = loxi_utils.list_to_entry_type(cls)
1461 gen_list_first(out, cls, e_type)
1462 gen_list_next(out, cls, e_type)
1463 gen_list_append(out, cls, e_type)
1464
1465################################################################
1466#
1467# Accessor Functions
1468#
1469################################################################
1470
Andreas Wundsam53256162013-05-02 14:05:53 -07001471
Rich Lanea06d0c32013-03-25 08:52:03 -07001472def gen_accessor_declarations(out):
1473 """
1474 Generate the declaration of each version independent accessor
1475
1476 @param out The file to which to write the decs
1477 """
1478
1479 out.write("""
1480/****************************************************************
1481 *
1482 * Unified, per-member accessor function declarations
1483 *
1484 ****************************************************************/
1485""")
1486 for cls in of_g.standard_class_order:
1487 if cls in type_maps.inheritance_map:
1488 continue
1489 out.write("\n/* Unified accessor functions for %s */\n" % cls)
1490 for m_name in of_g.ordered_members[cls]:
1491 if m_name in of_g.skip_members:
1492 continue
1493 m_type = loxi_utils.member_base_type(cls, m_name)
1494 base_name = "%s_%s" % (cls, m_name)
1495 gparams = ",\n ".join(param_list(cls, m_name, "get"))
1496 get_ret_type = accessor_return_type("get", m_type)
1497 sparams = ",\n ".join(param_list(cls, m_name, "set"))
1498 set_ret_type = accessor_return_type("set", m_type)
1499 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1500 bind_ret_type = accessor_return_type("bind", m_type)
1501
1502 if loxi_utils.type_is_of_object(m_type):
1503 # Generate bind accessors, but not get accessor
1504 out.write("""
1505extern %(set_ret_type)s %(base_name)s_set(
1506 %(sparams)s);
1507extern %(bind_ret_type)s %(base_name)s_bind(
1508 %(bparams)s);
1509extern %(m_type)s *%(cls)s_%(m_name)s_get(
1510 %(cls)s_t *obj);
1511""" % dict(base_name=base_name, sparams=sparams, bparams=bparams,
1512 m_name=m_name, m_type=m_type, cls=cls,
1513 set_ret_type=set_ret_type, bind_ret_type=bind_ret_type))
1514 else:
1515 out.write("""
1516extern %(set_ret_type)s %(base_name)s_set(
1517 %(sparams)s);
1518extern %(get_ret_type)s %(base_name)s_get(
1519 %(gparams)s);
1520""" % dict(base_name=base_name, gparams=gparams, sparams=sparams,
1521 get_ret_type=get_ret_type, set_ret_type=set_ret_type))
Andreas Wundsam53256162013-05-02 14:05:53 -07001522
Rich Lanea06d0c32013-03-25 08:52:03 -07001523 if loxi_utils.class_is_list(cls):
1524 e_type = loxi_utils.list_to_entry_type(cls)
1525 out.write("""
1526extern int %(cls)s_first(
1527 %(cls)s_t *list, %(e_type)s_t *obj);
1528extern int %(cls)s_next(
1529 %(cls)s_t *list, %(e_type)s_t *obj);
1530extern int %(cls)s_append_bind(
1531 %(cls)s_t *list, %(e_type)s_t *obj);
1532extern int %(cls)s_append(
1533 %(cls)s_t *list, %(e_type)s_t *obj);
1534
1535/**
1536 * Iteration macro for list of type %(cls)s
1537 * @param list Pointer to the list being iterated over of
1538 * type %(cls)s
1539 * @param elt Pointer to an element of type %(e_type)s
1540 * @param rv On exiting the loop will have the value OF_ERROR_RANGE.
1541 */
1542#define %(u_cls)s_ITER(list, elt, rv) \\
1543 for ((rv) = %(cls)s_first((list), (elt)); \\
1544 (rv) == OF_ERROR_NONE; \\
1545 (rv) = %(cls)s_next((list), (elt)))
1546""" % dict(u_cls=cls.upper(), cls=cls, e_type=e_type))
1547
1548
1549def wire_accessor(m_type, a_type):
1550 """
1551 Returns the name of the a_type accessor for low level wire buff offset
1552 @param m_type The member type
1553 @param a_type The accessor type (set or get)
1554 """
1555 # Strip off _t if present
1556 if m_type in of_g.of_base_types:
1557 m_type = of_g.of_base_types[m_type]["short_name"]
1558 if m_type in of_g.of_mixed_types:
1559 m_type = of_g.of_mixed_types[m_type]["short_name"]
1560 if m_type[-2:] == "_t":
1561 m_type = m_type[:-2]
1562 if m_type == "octets":
1563 m_type = "octets_data"
1564 return "of_wire_buffer_%s_%s" % (m_type, a_type)
1565
1566def get_len_macro(cls, m_type, version):
1567 """
1568 Get the length macro for m_type in cls
1569 """
1570 if m_type.find("of_match") == 0:
1571 return "_WIRE_MATCH_PADDED_LEN(obj, offset)"
1572 if m_type.find("of_list_oxm") == 0:
1573 return "wire_match_len(obj, 0) - 4"
1574 if loxi_utils.class_is_tlv16(m_type):
1575 return "_TLV16_LEN(obj, offset)"
1576 if cls == "of_packet_out" and m_type == "of_list_action_t":
1577 return "_PACKET_OUT_ACTION_LEN(obj)"
1578 # Default is everything to the end of the object
1579 return "_END_LEN(obj, offset)"
1580
1581def gen_accessor_offsets(out, cls, m_name, version, a_type, m_type, offset):
1582 """
1583 Generate the sub-object offset and length calculations for accessors
1584 @param out File being written
1585 @param m_name Name of member
1586 @param version Wire version being processed
1587 @param a_type The accessor type (set or get)
1588 @param m_type The original member type
1589 @param offset The offset of the object or -1 if not fixed
1590 """
1591 # determine offset
1592 o_str = "%d" % offset # Default is fixed length
1593 if offset == -1:
1594 # There are currently 4 special cases for this
1595 # In general, get offset and length of predecessor
1596 if (loxi_utils.cls_is_flow_mod(cls) and m_name == "instructions"):
1597 pass
1598 elif (cls == "of_flow_stats_entry" and m_name == "instructions"):
1599 pass
1600 elif (cls == "of_packet_in" and m_name == "data"):
1601 pass
1602 elif (cls == "of_packet_out" and m_name == "data"):
1603 pass
1604 else:
1605 debug("Error: Unknown member with offset == -1")
1606 debug(" cls %s, m_name %s, version %d" % (cls, m_name, version))
1607 sys.exit(1)
1608 o_str = "_%s_%s_OFFSET(obj)" % (cls.upper()[3:], m_name.upper())
1609
1610 out.write("""\
1611 offset = %s;
1612""" % o_str);
1613
1614 # This could be moved to main body but for version check
1615 if not loxi_utils.type_is_scalar(m_type):
1616 if loxi_utils.class_is_var_len(m_type[:-2], version) or \
1617 m_type == "of_match_t":
1618 len_macro = get_len_macro(cls, m_type, version)
1619 else:
1620 len_macro = "%d" % of_g.base_length[(m_type[:-2], version)]
1621 out.write(" cur_len = %s;\n" % len_macro)
1622 out.write(" break;\n")
1623
1624def length_of(m_type, version):
1625 """
1626 Return the length of a type based on the version
1627 """
1628 if m_type in of_g.of_mixed_types:
1629 m_type = of_g.of_mixed_types[m_type][version]
1630 if m_type in of_g.of_base_types:
1631 return of_g.of_base_types[m_type]["bytes"]
1632 if (m_type[:-2], version) in of_g.base_length:
1633 return of_g.base_length[(m_type[:-2], version)]
1634 print "Unknown length request", m_type, version
1635 sys.exit(1)
Andreas Wundsam53256162013-05-02 14:05:53 -07001636
Rich Lanea06d0c32013-03-25 08:52:03 -07001637
1638def gen_get_accessor_body(out, cls, m_type, m_name):
1639 """
1640 Generate the common operations for a get accessor
1641 """
1642 if loxi_utils.type_is_scalar(m_type):
1643 ver = "" # See if version required for scalar update
1644 if m_type in of_g.of_mixed_types:
1645 ver = "ver, "
1646 out.write("""\
1647 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s);
1648""" % dict(wa=wire_accessor(m_type, "get"), ver=ver, m_name=m_name))
1649
1650 if m_type == "of_port_no_t":
1651 out.write(" OF_PORT_NO_VALUE_CHECK(*%s, ver);\n" % m_name)
1652 elif m_type == "of_octets_t":
1653 out.write("""\
1654 ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
1655 %(m_name)s->bytes = cur_len;
1656 %(m_name)s->data = OF_WIRE_BUFFER_INDEX(wbuf, abs_offset);
1657""" % dict(m_name=m_name))
1658 elif m_type == "of_match_t":
1659 out.write("""
1660 ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
1661 match_octets.bytes = cur_len;
1662 match_octets.data = OF_OBJECT_BUFFER_INDEX(obj, offset);
1663 OF_TRY(of_match_deserialize(ver, %(m_name)s, &match_octets));
1664""" % dict(m_name=m_name))
1665 else:
1666 out.write("""
1667 /* Initialize child */
1668 %(m_type)s_init(%(m_name)s, obj->version, 0, 1);
1669 /* Attach to parent */
1670 %(m_name)s->parent = (of_object_t *)obj;
1671 %(m_name)s->wire_object.wbuf = obj->wire_object.wbuf;
1672 %(m_name)s->wire_object.obj_offset = abs_offset;
1673 %(m_name)s->wire_object.owned = 0;
1674 %(m_name)s->length = cur_len;
1675""" % dict(m_type=m_type[:-2], m_name=m_name))
1676
1677
1678def gen_set_accessor_body(out, cls, m_type, m_name):
1679 """
1680 Generate the contents of a set accessor
1681 """
1682 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1683 ver = "" # See if version required for scalar update
1684 if m_type in of_g.of_mixed_types:
1685 ver = "ver, "
1686 cur_len = "" # See if version required for scalar update
1687 if m_type == "of_octets_t":
1688 cur_len = ", cur_len"
1689 out.write("""\
1690 new_len = %(m_name)s->bytes;
1691 of_wire_buffer_grow(wbuf, abs_offset + (new_len - cur_len));
1692""" % dict(m_name=m_name))
1693 out.write("""\
1694 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s%(cur_len)s);
1695""" % dict(wa=wire_accessor(m_type, "set"), ver=ver, cur_len=cur_len,
1696 m_name=m_name))
1697
1698 elif m_type == "of_match_t":
1699 out.write("""
1700 /* Match object */
1701 OF_TRY(of_match_serialize(ver, %(m_name)s, &match_octets));
1702 new_len = match_octets.bytes;
1703 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1704 match_octets.data, new_len);
1705 /* Free match serialized octets */
1706 FREE(match_octets.data);
1707""" % dict(m_name=m_name))
1708
1709 else: # Other object type
1710 out.write("\n /* LOCI object type */")
1711 # Need to special case OXM list
1712 out.write("""
1713 new_len = %(m_name)s->length;
1714 /* If underlying buffer already shared; nothing to do */
1715 if (obj->wire_object.wbuf == %(m_name)s->wire_object.wbuf) {
1716 of_wire_buffer_grow(wbuf, abs_offset + new_len);
1717 /* Verify that the offsets are correct */
1718 ASSERT(abs_offset == OF_OBJECT_ABSOLUTE_OFFSET(%(m_name)s, 0));
1719 /* ASSERT(new_len == cur_len); */ /* fixme: may fail for OXM lists */
1720 return %(ret_success)s;
1721 }
1722
1723 /* Otherwise, replace existing object in data buffer */
1724 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1725 OF_OBJECT_BUFFER_INDEX(%(m_name)s, 0), new_len);
1726""" % dict(m_name=m_name, ret_success=accessor_return_success("set", m_type)))
1727
1728 if not loxi_utils.type_is_scalar(m_type):
1729 if cls == "of_packet_out" and m_type == "of_list_action_t":
1730 out.write("""
1731 /* Special case for setting action lengths */
1732 _PACKET_OUT_ACTION_LEN_SET(obj, %(m_name)s->length);
1733""" % dict(m_name=m_name))
1734 elif m_type not in ["of_match_t", "of_octets_t"]:
1735 out.write("""
1736 /* @fixme Shouldn't this precede copying value's data to buffer? */
1737 if (%(m_name)s->wire_length_set != NULL) {
1738 %(m_name)s->wire_length_set((of_object_t *)%(m_name)s, %(m_name)s->length);
1739 }
1740""" % dict(m_name=m_name))
1741 out.write("""
1742 /* Not scalar, update lengths if needed */
1743 delta = new_len - cur_len;
1744 if (delta != 0) {
1745 /* Update parent(s) */
1746 of_object_parent_length_update((of_object_t *)obj, delta);
1747 }
1748""")
1749
1750def obj_assert_check(cls):
1751 """
1752 The body of the assert check for an accessor
1753 We allow all versions of add/delete/modify to use the same accessors
1754 """
1755 if cls in ["of_flow_modify", "of_flow_modify_strict",
1756 "of_flow_delete", "of_flow_delete_strict",
1757 "of_flow_add"]:
1758 return "IS_FLOW_MOD_SUBTYPE(obj->object_id)"
1759 else:
1760 return "obj->object_id == %s" % cls.upper()
1761
1762def gen_of_object_get(out, cls, m_name, m_type):
1763 sub_cls = m_type[:-2]
1764 out.write("""
1765/**
Andreas Wundsam53256162013-05-02 14:05:53 -07001766 * Create a copy of %(m_name)s into a new variable of type %(m_type)s from
Rich Lanea06d0c32013-03-25 08:52:03 -07001767 * a %(cls)s instance.
1768 *
1769 * @param obj Pointer to the source of type %(cls)s_t
1770 * @returns A pointer to a new instance of type %(m_type)s whose contents
1771 * match that of %(m_name)s from source
1772 * @returns NULL if an error occurs
1773 */
1774%(m_type)s *
1775%(cls)s_%(m_name)s_get(%(cls)s_t *obj) {
1776 %(m_type)s _%(m_name)s;
1777 %(m_type)s *_%(m_name)s_ptr;
1778
1779 %(cls)s_%(m_name)s_bind(obj, &_%(m_name)s);
1780 _%(m_name)s_ptr = (%(m_type)s *)of_object_dup(&_%(m_name)s);
1781 return _%(m_name)s_ptr;
1782}
1783""" % dict(m_name=m_name, m_type=m_type, cls=cls, sub_cls=sub_cls))
1784
1785def gen_unified_acc_body(out, cls, m_name, ver_type_map, a_type, m_type):
1786 """
1787 Generate the body of a set or get accessor function
1788
1789 @param out The file to which to write the decs
1790 @param cls The class name
1791 @param m_name The member name
1792 @param ver_type_map Maps (type, offset) pairs to a list of versions
1793 @param a_type The accessor type, set or get
1794 @param m_type The original member type
1795
1796 The type values in ver_type_map are now ignored as we've pushed down
1797 the type munging to the lower level.
1798
1799 This is unified because the version switch case processing is the
1800 same for both set and get
1801 """
1802 out.write("""{
1803 of_wire_buffer_t *wbuf;
1804 int offset = 0; /* Offset of value relative to the start obj */
1805 int abs_offset; /* Offset of value relative to start of wbuf */
1806 of_version_t ver;
1807""")
1808
1809 if not loxi_utils.type_is_scalar(m_type):
1810 out.write("""\
1811 int cur_len = 0; /* Current length of object data */
1812""")
1813 if a_type == "set":
1814 out.write("""\
1815 int new_len, delta; /* For set, need new length and delta */
1816""")
1817
1818 # For match, need octet string for set/get
1819 if m_type == "of_match_t":
1820 out.write("""\
1821 of_octets_t match_octets; /* Serialized string for match */
1822""")
1823
1824 out.write("""
1825 ASSERT(%(assert_str)s);
1826 ver = obj->version;
1827 wbuf = OF_OBJECT_TO_WBUF(obj);
1828 ASSERT(wbuf != NULL);
1829
1830 /* By version, determine offset and current length (where needed) */
1831 switch (ver) {
1832""" % dict(assert_str=obj_assert_check(cls)))
1833
1834 for first in sorted(ver_type_map):
1835 (prev_t, prev_o) = ver_type_map[first]
1836 prev_len = length_of(prev_t, first)
1837 prev = first
1838 out.write(" case %s:\n" % of_g.wire_ver_map[first])
1839 break
1840
1841 for next in sorted(ver_type_map):
1842 if next == first:
1843 continue
1844
1845 (t, o) = ver_type_map[next]
1846 cur_len = length_of(t, next)
1847 if o == prev_o and cur_len == prev_len:
1848 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1849 continue
1850 gen_accessor_offsets(out, cls, m_name, prev, a_type, m_type, prev_o)
1851 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1852 (prev_t, prev_o, prev_len, prev) = (t, o, cur_len, next)
1853
1854 gen_accessor_offsets(out, cls, m_name, next, a_type, m_type, prev_o)
1855 out.write("""\
1856 default:
1857 ASSERT(0);
1858 }
1859
1860 abs_offset = OF_OBJECT_ABSOLUTE_OFFSET(obj, offset);
1861 ASSERT(abs_offset >= 0);
1862""")
1863 if not loxi_utils.type_is_scalar(m_type):
1864 out.write(" ASSERT(cur_len >= 0 && cur_len < 64 * 1024);\n")
1865
1866 # Now generate the common accessor code
1867 if a_type in ["get", "bind"]:
1868 gen_get_accessor_body(out, cls, m_type, m_name)
1869 else:
1870 gen_set_accessor_body(out, cls, m_type, m_name)
1871
1872 out.write("""
1873 OF_LENGTH_CHECK_ASSERT(obj);
1874
1875 return %s;
1876}
1877""" % accessor_return_success(a_type, m_type))
1878
1879def gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map):
1880 """
1881 For generating the bind call for OF sub-objects
1882 """
1883
1884 params = ",\n ".join(param_list(cls, m_name, "bind"))
1885 out.write("""
1886/**
1887 * Bind an object of type %(m_type)s to the parent of type %(cls)s for
1888 * member %(m_name)s
1889 * @param obj Pointer to an object of type %(cls)s.
1890 * @param %(m_name)s Pointer to the child object of type
1891 * %(m_type)s to be filled out.
1892 * \ingroup %(cls)s
1893 *
1894 * The parameter %(m_name)s is filled out to point to the same underlying
1895 * wire buffer as its parent.
1896 *
1897 */
1898""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1899
1900 ret_type = accessor_return_type("bind", m_type)
1901 out.write("%s\n%s_%s_bind(\n %s)\n" % (ret_type, cls, m_name, params))
1902 gen_unified_acc_body(out, cls, m_name, ver_type_map, "bind", m_type)
1903
1904def gen_get_accessor(out, cls, m_name, m_type, ver_type_map):
1905 """
1906 For generating the get call for non- OF sub-objects
1907 """
1908 params = ",\n ".join(param_list(cls, m_name, "get"))
1909 out.write("""
1910/**
1911 * Get %(m_name)s from an object of type %(cls)s.
1912 * @param obj Pointer to an object of type %(cls)s.
1913 * @param %(m_name)s Pointer to the child object of type
1914 * %(m_type)s to be filled out.
1915 *
1916 */
1917""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1918
1919 ret_type = accessor_return_type("get", m_type)
1920 out.write("%s\n%s_%s_get(\n %s)\n" % (ret_type, cls, m_name, params))
1921 gen_unified_acc_body(out, cls, m_name, ver_type_map, "get", m_type)
1922
Rich Lanece2e4642013-12-15 12:05:45 -08001923def gen_accessor_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07001924 for m_name in of_g.ordered_members[cls]:
1925 if m_name in of_g.skip_members:
1926 continue
1927 m_type = loxi_utils.member_base_type(cls, m_name)
1928 ver_type_map = field_ver_get(cls, m_name)
1929
1930 # Generate get/bind pending on member type
1931 # FIXME: Does this do the right thing for match?
1932 if loxi_utils.type_is_of_object(m_type):
1933 gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map)
1934 gen_of_object_get(out, cls, m_name, m_type)
1935 else:
1936 gen_get_accessor(out, cls, m_name, m_type, ver_type_map)
1937
1938 # Now generate set accessor for all objects
1939 params = ",\n ".join(param_list(cls, m_name, "set"))
1940 out.write("""
1941/**
1942 * Set %(m_name)s in an object of type %(cls)s.
1943 * @param obj Pointer to an object of type %(cls)s.
1944""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1945 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1946 out.write("""\
1947 * @param %(m_name)s The value to write into the object
1948 */
1949""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1950 else:
1951 out.write("""\
1952 * @param %(m_name)s Pointer to the child of type %(m_type)s.
1953 *
1954 * If the child's wire buffer is the same as the parent's, then
1955 * nothing is done as the changes have already been registered in the
1956 * parent. Otherwise, the data in the child's wire buffer is inserted
1957 * into the parent's and the appropriate lengths are updated.
1958 */
1959""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1960 ret_type = accessor_return_type("set", m_type)
1961 out.write("%s\n%s_%s_set(\n %s)\n" % (ret_type, cls, m_name, params))
1962 gen_unified_acc_body(out, cls, m_name, ver_type_map, "set", m_type)
1963
Rich Lanea06d0c32013-03-25 08:52:03 -07001964################################################################
1965#
1966# New/Delete Function Definitions
1967#
1968################################################################
1969
1970
1971################################################################
1972# First, some utility functions for new/delete
1973################################################################
1974
1975def del_function_proto(cls):
1976 """
1977 Return the prototype for the delete operator for the given class
1978 @param cls The class name
1979 """
1980 fn = "void\n"
1981 return fn
1982
1983
Rich Lanea06d0c32013-03-25 08:52:03 -07001984################################################################
1985# Routines to generate the body of new/delete functions
1986################################################################
1987
1988def gen_init_fn_body(cls, out):
1989 """
1990 Generate function body for init function
1991 @param cls The class name for the function
1992 @param out The file to which to write
1993 """
1994 if cls in type_maps.inheritance_map:
1995 param = "obj_p"
1996 else:
1997 param = "obj"
1998
1999 out.write("""
2000/**
2001 * Initialize an object of type %(cls)s.
2002 *
2003 * @param obj Pointer to the object to initialize
2004 * @param version The wire version to use for the object
2005 * @param bytes How many bytes in the object
2006 * @param clean_wire Boolean: If true, clear the wire object control struct
2007 *
2008 * If bytes < 0, then the default fixed length is used for the object
2009 *
2010 * This is a "coerce" function that sets up the pointers for the
Andreas Wundsam53256162013-05-02 14:05:53 -07002011 * accessors properly.
Rich Lanea06d0c32013-03-25 08:52:03 -07002012 *
2013 * If anything other than 0 is passed in for the buffer size, the underlying
2014 * wire buffer will have 'grow' called.
2015 */
2016
2017void
2018%(cls)s_init(%(cls)s_t *%(param)s,
2019 of_version_t version, int bytes, int clean_wire)
2020{
2021""" % dict(cls=cls, param=param))
2022
2023 # Use an extra pointer to deal with inheritance classes
2024 if cls in type_maps.inheritance_map:
2025 out.write("""\
2026 %s_header_t *obj;
2027
2028 obj = &obj_p->header; /* Need instantiable subclass */
2029""" % cls)
2030
2031 out.write("""
2032 ASSERT(of_object_fixed_len[version][%(enum)s] >= 0);
2033 if (clean_wire) {
2034 MEMSET(obj, 0, sizeof(*obj));
2035 }
2036 if (bytes < 0) {
Rich Lanef70be942013-07-18 13:33:14 -07002037 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002038 }
2039 obj->version = version;
2040 obj->length = bytes;
2041 obj->object_id = %(enum)s;
2042""" % dict(cls=cls, enum=enum_name(cls)))
2043 gen_coerce_ops(out, cls)
2044
2045 out.write("""
2046 /* Grow the wire buffer */
2047 if (obj->wire_object.wbuf != NULL) {
2048 int tot_bytes;
2049
2050 tot_bytes = bytes + obj->wire_object.obj_offset;
2051 of_wire_buffer_grow(obj->wire_object.wbuf, tot_bytes);
2052 }
2053}
2054
2055""")
2056
2057## @fixme This should also be updated once there is a map from
2058# class instance to wire length/type accessors
2059def gen_wire_push_fn(cls, out):
2060 """
2061 Generate the calls to push values into the wire buffer
2062 """
2063 if type_maps.class_is_virtual(cls):
2064 print "Push fn gen called for virtual class " + cls
2065 return
2066
2067 out.write("""
2068/**
2069 * Helper function to push values into the wire buffer
2070 */
2071static inline int
2072%(cls)s_push_wire_values(%(cls)s_t *obj)
2073{
2074""" % dict(cls=cls))
2075
Rich Lanebdd8e292013-12-06 17:37:39 -08002076 import loxi_globals
2077 uclass = loxi_globals.unified.class_by_name(cls)
2078 if uclass and not uclass.virtual and uclass.has_type_members:
2079 out.write("""
2080 %(cls)s_push_wire_types(obj);
2081""" % dict(cls=cls))
2082
Rich Lanea06d0c32013-03-25 08:52:03 -07002083 if loxi_utils.class_is_message(cls):
2084 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002085 /* Message obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002086 of_message_t msg;
2087
2088 if ((msg = OF_OBJECT_TO_MESSAGE(obj)) != NULL) {
Rich Lanea06d0c32013-03-25 08:52:03 -07002089 of_message_length_set(msg, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002090 }
2091""" % dict(name = enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002092
Rich Lanea06d0c32013-03-25 08:52:03 -07002093 else: # Not a message
2094 if loxi_utils.class_is_tlv16(cls):
2095 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002096 /* TLV obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002097 of_tlv16_wire_length_set((of_object_t *)obj, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002098""" % dict(enum=enum_name(cls)))
Rich Lanea06d0c32013-03-25 08:52:03 -07002099
Rich Lanea06d0c32013-03-25 08:52:03 -07002100 if loxi_utils.class_is_u16_len(cls) or cls == "of_packet_queue":
2101 out.write("""
2102 obj->wire_length_set((of_object_t *)obj, obj->length);
2103""")
2104
2105 if cls == "of_meter_stats":
2106 out.write("""
2107 of_meter_stats_wire_length_set((of_object_t *)obj, obj->length);
2108""" % dict(enum=enum_name(cls)))
2109
2110 out.write("""
2111 return OF_ERROR_NONE;
2112}
2113""")
2114
2115def gen_new_fn_body(cls, out):
2116 """
2117 Generate function body for new function
2118 @param cls The class name for the function
2119 @param out The file to which to write
2120 """
2121
2122 out.write("""
2123/**
2124 * \\defgroup %(cls)s %(cls)s
2125 */
2126""" % dict(cls=cls))
2127
2128 if not type_maps.class_is_virtual(cls):
2129 gen_wire_push_fn(cls, out)
2130
2131 out.write("""
2132/**
2133 * Create a new %(cls)s object
2134 *
2135 * @param version The wire version to use for the object
2136 * @return Pointer to the newly create object or NULL on error
2137 *
2138 * Initializes the new object with it's default fixed length associating
2139 * a new underlying wire buffer.
2140 *
2141 * Use new_from_message to bind an existing message to a message object,
2142 * or a _get function for non-message objects.
2143 *
2144 * \\ingroup %(cls)s
2145 */
2146
2147%(cls)s_t *
2148%(cls)s_new_(of_version_t version)
2149{
2150 %(cls)s_t *obj;
2151 int bytes;
2152
Rich Lanef70be942013-07-18 13:33:14 -07002153 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002154
2155 /* Allocate a maximum-length wire buffer assuming we'll be appending to it. */
2156 if ((obj = (%(cls)s_t *)of_object_new(OF_WIRE_BUFFER_MAX_LENGTH)) == NULL) {
2157 return NULL;
2158 }
2159
2160 %(cls)s_init(obj, version, bytes, 0);
2161""" % dict(cls=cls, enum=enum_name(cls)))
2162 if not type_maps.class_is_virtual(cls):
2163 out.write("""
2164 if (%(cls)s_push_wire_values(obj) < 0) {
2165 FREE(obj);
2166 return NULL;
2167 }
2168""" % dict(cls=cls))
2169
2170 match_offset = v3_match_offset_get(cls)
2171 if match_offset >= 0:
2172 # Init length field for match object
2173 out.write("""
2174 /* Initialize match TLV for 1.2 */
2175 /* FIXME: Check 1.3 below */
2176 if ((version == OF_VERSION_1_2) || (version == OF_VERSION_1_3)) {
2177 of_object_u16_set((of_object_t *)obj, %(match_offset)d + 2, 4);
2178 }
2179""" % dict(match_offset=match_offset))
2180 out.write("""
2181 return obj;
2182}
2183
2184#if defined(OF_OBJECT_TRACKING)
2185
2186/*
2187 * Tracking objects. Call the new function and then record location
2188 */
2189
2190%(cls)s_t *
2191%(cls)s_new_tracking(of_version_t version,
2192 const char *file, int line)
2193{
2194 %(cls)s_t *obj;
2195
2196 obj = %(cls)s_new_(version);
2197 of_object_track((of_object_t *)obj, file, line);
2198
2199 return obj;
2200}
2201#endif
2202""" % dict(cls=cls))
2203
2204
2205def gen_from_message_fn_body(cls, out):
2206 """
2207 Generate function body for from_message function
2208 @param cls The class name for the function
2209 @param out The file to which to write
2210 """
2211 out.write("""
2212/**
2213 * Create a new %(cls)s object and bind it to an existing message
2214 *
2215 * @param msg The message to bind the new object to
2216 * @return Pointer to the newly create object or NULL on error
2217 *
2218 * \ingroup %(cls)s
2219 */
2220
2221%(cls)s_t *
2222%(cls)s_new_from_message_(of_message_t msg)
2223{
2224 %(cls)s_t *obj = NULL;
2225 of_version_t version;
2226 int length;
2227
2228 if (msg == NULL) return NULL;
2229
2230 version = of_message_version_get(msg);
2231 if (!OF_VERSION_OKAY(version)) return NULL;
2232
2233 length = of_message_length_get(msg);
2234
2235 if ((obj = (%(cls)s_t *)of_object_new(-1)) == NULL) {
2236 return NULL;
2237 }
2238
2239 %(cls)s_init(obj, version, 0, 0);
2240
2241 if ((of_object_buffer_bind((of_object_t *)obj, OF_MESSAGE_TO_BUFFER(msg),
2242 length, OF_MESSAGE_FREE_FUNCTION)) < 0) {
2243 FREE(obj);
2244 return NULL;
2245 }
2246 obj->length = length;
2247 obj->version = version;
2248
2249 return obj;
2250}
2251
2252#if defined(OF_OBJECT_TRACKING)
2253
2254/*
2255 * Tracking objects. Call the new function and then record location
2256 */
2257
2258%(cls)s_t *
2259%(cls)s_new_from_message_tracking(of_message_t msg,
2260 const char *file, int line)
2261{
2262 %(cls)s_t *obj;
2263
2264 obj = %(cls)s_new_from_message_(msg);
2265 of_object_track((of_object_t *)obj, file, line);
2266
2267 return obj;
2268}
2269#endif
2270""" % dict(cls=cls))
2271
2272
2273################################################################
2274# Now the top level generator functions
2275################################################################
2276
2277def gen_new_function_declarations(out):
2278 """
2279 Gerenate the header file declarations for new operators for all classes
2280 @param out The file to which to write the decs
2281 """
2282
2283 out.write("""
2284/****************************************************************
2285 *
2286 * New operator declarations
2287 *
2288 * _new: Create a new object for writing; includes init
2289 * _new_from_message: Create a new instance of the object and bind the
2290 * message data to the object
2291 * _init: Initialize and optionally allocate buffer space for an
2292 * automatic instance
2293 *
2294 * _new and _from_message require a delete operation to be called
2295 * on the object.
2296 *
2297 ****************************************************************/
2298""")
2299 out.write("""
2300/*
2301 * If object tracking is enabled, map "new" and "new from msg"
2302 * calls to tracking versions; otherwise, directly to internal
2303 * versions of fns which have the same name but end in _.
2304 */
2305
2306#if defined(OF_OBJECT_TRACKING)
2307""")
2308 for cls in of_g.standard_class_order:
2309 out.write("""
2310extern %(cls)s_t *
2311 %(cls)s_new_tracking(of_version_t version,
2312 const char *file, int line);
2313#define %(cls)s_new(version) \\
2314 %(cls)s_new_tracking(version, \\
2315 __FILE__, __LINE__)
2316""" % dict(cls=cls))
2317 if loxi_utils.class_is_message(cls):
2318 out.write("""extern %(cls)s_t *
2319 %(cls)s_new_from_message_tracking(of_message_t msg,
2320 const char *file, int line);
2321#define %(cls)s_new_from_message(msg) \\
2322 %(cls)s_new_from_message_tracking(msg, \\
2323 __FILE__, __LINE__)
2324""" % dict(cls=cls))
2325
2326 out.write("""
2327#else /* No object tracking */
2328""")
2329 for cls in of_g.standard_class_order:
2330 out.write("""
2331#define %(cls)s_new(version) \\
2332 %(cls)s_new_(version)
2333""" % dict(cls=cls))
2334 if loxi_utils.class_is_message(cls):
2335 out.write("""#define %(cls)s_new_from_message(msg) \\
2336 %(cls)s_new_from_message_(msg)
2337""" % dict(cls=cls))
2338
2339 out.write("""
2340#endif /* Object tracking */
2341""")
2342
2343 for cls in of_g.standard_class_order:
2344 out.write("""
2345extern %(cls)s_t *
2346 %(cls)s_new_(of_version_t version);
2347""" % dict(cls=cls))
2348 if loxi_utils.class_is_message(cls):
2349 out.write("""extern %(cls)s_t *
2350 %(cls)s_new_from_message_(of_message_t msg);
2351""" % dict(cls=cls))
2352 out.write("""extern void %(cls)s_init(
2353 %(cls)s_t *obj, of_version_t version, int bytes, int clean_wire);
2354""" % dict(cls=cls))
2355
2356 out.write("""
2357/****************************************************************
2358 *
2359 * Delete operator static inline definitions.
2360 * These are here for type checking purposes only
2361 *
2362 ****************************************************************/
2363""")
2364 for cls in of_g.standard_class_order:
2365# if cls in type_maps.inheritance_map:
2366# continue
2367 out.write("""
2368/**
2369 * Delete an object of type %(cls)s_t
2370 * @param obj An instance of type %(cls)s_t
2371 *
2372 * \ingroup %(cls)s
2373 */
2374static inline void
2375%(cls)s_delete(%(cls)s_t *obj) {
2376 of_object_delete((of_object_t *)(obj));
2377}
2378""" % dict(cls=cls))
2379
2380 out.write("""
2381typedef void (*of_object_init_f)(of_object_t *obj, of_version_t version,
2382 int bytes, int clean_wire);
Rich Laneb157b0f2013-03-27 13:55:28 -07002383extern const of_object_init_f of_object_init_map[];
Rich Lanea06d0c32013-03-25 08:52:03 -07002384""")
2385
2386 out.write("""
2387/****************************************************************
2388 *
2389 * Function pointer initialization functions
2390 * These are part of the "coerce" type casting for objects
2391 *
2392 ****************************************************************/
2393""")
2394
2395#
2396# @fixme Not clear that these should all be set for virtual fns
2397#
2398# @fixme Clean up. should have a (language specific) map from class
2399# to length and type get/set functions
2400#
2401
2402def gen_coerce_ops(out, cls):
2403 out.write("""
2404 /* Set up the object's function pointers */
2405""")
2406
Rich Lane92feca82013-12-10 15:57:13 -08002407 uclass = loxi_globals.unified.class_by_name(cls)
2408 if uclass and not uclass.virtual and uclass.has_type_members:
2409 out.write("""
2410 obj->wire_type_set = %(cls)s_push_wire_types;
2411""" % dict(cls=cls))
2412
Rich Lanea06d0c32013-03-25 08:52:03 -07002413 if loxi_utils.class_is_message(cls):
2414 out.write("""
2415 obj->wire_length_get = of_object_message_wire_length_get;
2416 obj->wire_length_set = of_object_message_wire_length_set;
2417""")
2418 else:
2419 if loxi_utils.class_is_tlv16(cls):
2420 if not (cls in type_maps.inheritance_map): # Don't set for super
2421 out.write("""
2422 obj->wire_length_set = of_tlv16_wire_length_set;
Rich Lanea06d0c32013-03-25 08:52:03 -07002423""")
2424 out.write("""
2425 obj->wire_length_get = of_tlv16_wire_length_get;
2426""")
2427 if loxi_utils.class_is_action(cls):
2428 out.write("""
2429 obj->wire_type_get = of_action_wire_object_id_get;
2430""")
2431 if loxi_utils.class_is_action_id(cls):
2432 out.write("""
2433 obj->wire_type_get = of_action_id_wire_object_id_get;
2434""")
2435 if loxi_utils.class_is_instruction(cls):
2436 out.write("""
2437 obj->wire_type_get = of_instruction_wire_object_id_get;
2438""")
2439 if loxi_utils.class_is_queue_prop(cls):
2440 out.write("""
2441 obj->wire_type_get = of_queue_prop_wire_object_id_get;
2442""")
2443 if loxi_utils.class_is_table_feature_prop(cls):
2444 out.write("""
2445 obj->wire_type_get = of_table_feature_prop_wire_object_id_get;
2446""")
2447 if loxi_utils.class_is_meter_band(cls):
2448 out.write("""
2449 obj->wire_type_get = of_meter_band_wire_object_id_get;
2450""")
2451 if loxi_utils.class_is_hello_elem(cls):
2452 out.write("""
2453 obj->wire_type_get = of_hello_elem_wire_object_id_get;
2454""")
2455 if loxi_utils.class_is_oxm(cls):
2456 out.write("""
2457 obj->wire_length_get = of_oxm_wire_length_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002458 obj->wire_type_get = of_oxm_wire_object_id_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002459""")
2460 if loxi_utils.class_is_u16_len(cls):
2461 out.write("""
2462 obj->wire_length_get = of_u16_len_wire_length_get;
2463 obj->wire_length_set = of_u16_len_wire_length_set;
2464""")
2465 if cls == "of_packet_queue":
2466 out.write("""
2467 obj->wire_length_get = of_packet_queue_wire_length_get;
2468 obj->wire_length_set = of_packet_queue_wire_length_set;
2469""")
2470# if cls == "of_list_meter_band_stats":
2471# out.write("""
2472# obj->wire_length_get = of_list_meter_band_stats_wire_length_get;
2473#""")
2474 if cls == "of_meter_stats":
2475 out.write("""
2476 obj->wire_length_get = of_meter_stats_wire_length_get;
2477 obj->wire_length_set = of_meter_stats_wire_length_set;
2478""")
2479
Rich Laneb604e332013-12-15 13:23:51 -08002480def gen_new_function_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07002481 """
2482 Generate the new operator for all classes
2483
2484 @param out The file to which to write the functions
2485 """
2486
Rich Laneb604e332013-12-15 13:23:51 -08002487 gen_new_fn_body(cls, out)
2488 gen_init_fn_body(cls, out)
2489 if loxi_utils.class_is_message(cls):
2490 gen_from_message_fn_body(cls, out)
Rich Lanea06d0c32013-03-25 08:52:03 -07002491
Rich Lanea06d0c32013-03-25 08:52:03 -07002492"""
2493Document generation functions
2494
2495The main reason this is here is to generate documentation per
2496accessor that indicates the versions that support the interface.
2497"""
2498
2499
2500def gen_accessor_doc(out, name):
2501 """
2502 Generate documentation for each accessor function that indicates
2503 the versions supporting the accessor.
2504 """
2505
2506 common_top_matter(out, name)
2507
2508 out.write("/* DOCUMENTATION ONLY */\n")
2509
2510 for cls in of_g.standard_class_order:
2511 if cls in type_maps.inheritance_map:
2512 pass # Check this
2513
2514 out.write("""
2515/**
Andreas Wundsam53256162013-05-02 14:05:53 -07002516 * Structure for %(cls)s object. Get/set
Rich Lanea06d0c32013-03-25 08:52:03 -07002517 * accessors available in all versions unless noted otherwise
2518 *
2519""" % dict(cls=cls))
2520 if loxi_utils.class_is_list(cls):
2521 out.write("""\
2522 * @param first Function of type %(cls)s_first_f.
2523 * Setup a TBD class object to the first entry in the list
2524 * @param next Function of type %(cls)s_next_f.
2525 * Advance a TBD class object to the next entry in the list
2526 * @param append_bind Function of type %(cls)s_append_bind_f
2527 * Setup a TBD class object for append to the end of the current list
2528 * @param append Function of type @ref %(cls)s_append_f.
2529 * Copy an item to the end of a list
2530""" % dict(cls=cls))
2531
2532 for m_name in of_g.ordered_members[cls]:
2533 if m_name in of_g.skip_members:
2534 continue
2535 ver_type_map = field_ver_get(cls, m_name)
2536 (m_type, get_rv) = get_acc_rv(cls, m_name)
2537 if len(ver_type_map) == 3:
2538 # ver_string = "Available in all versions"
2539 ver_string = ""
2540 else:
2541 ver_string = "("
2542 for ver in sorted(ver_type_map):
2543 ver_string += " " + of_g.short_version_names[ver]
2544 ver_string += ")."
2545
2546 f_name = acc_name(cls, m_name)
2547 out.write("""\
2548 * @param %(m_name)s_get/set %(ver_string)s
2549 * Accessors for %(m_name)s, a variable of type %(m_type)s. Functions
2550 * are of type %(f_name)s_get_f and _set_f.
2551 *
2552""" % dict(f_name=f_name, m_name=m_name, ver_string=ver_string, m_type=m_type))
2553
2554 out.write("""\
2555 */
2556typedef struct %(cls)s_s %(cls)s_t;
2557""" % dict(cls=cls))
2558
2559 out.write("#endif /* _LOCI_DOC_H_ */\n")
2560
2561################################################################
2562#
2563# For fun, here are some unified, traditional C structure representation
2564#
2565################################################################
2566
2567def gen_cof_to_wire(out):
2568 pass
2569
2570def gen_wire_to_cof(out):
2571 pass
2572
2573def gen_cof_instance(out, cls):
2574 out.write("struct c%s_s {\n" % cls)
2575 for m in of_g.ordered_members[cls]:
2576 if m in of_g.skip_members:
2577 continue
2578 entry = of_g.unified[cls]["union"][m]
2579 cof_type = type_to_cof_type(entry["m_type"])
2580 out.write(" %-20s %s;\n" % (cof_type, m))
2581 out.write("};\n\n");
2582
2583def gen_cof_structs(out):
2584 """
2585 Generate non-version specific (common) representation of structures
2586
2587 @param out The file to which to write the functions
2588 """
2589
2590 out.write("\n/* Common, unified OpenFlow structure representations */\n")
2591 for cls in of_g.standard_class_order:
2592 if cls in type_maps.inheritance_map:
2593 continue
2594 gen_cof_instance(out, cls)
2595
2596################################################################
2597#
2598# Generate code samples for applications.
2599#
2600################################################################
2601
2602def gen_code_samples(out, name):
2603 out.write("""
2604#if 0 /* Do not compile in */
2605/**
2606 * @file %(name)s
2607 *
2608 * These are code samples for inclusion in other components
2609 */
2610
2611""" % dict(name=name))
2612
2613 gen_jump_table_template(out)
2614 # These are messages that a switch might expect.
2615 msg_list = ["of_echo_request",
2616 "of_hello",
2617 "of_packet_in",
2618 "of_packet_out",
2619 "of_port_mod",
2620 "of_port_stats_request",
2621 "of_queue_get_config_request",
2622 "of_queue_stats_request",
2623 "of_flow_add",
2624 "of_flow_modify",
2625 "of_flow_modify_strict",
2626 "of_flow_delete",
2627 "of_flow_delete_strict",
2628 "of_get_config_request",
2629 "of_flow_stats_request",
2630 "of_barrier_request",
2631 "of_echo_reply",
2632 "of_aggregate_stats_request",
2633 "of_desc_stats_request",
2634 "of_table_stats_request",
2635 "of_features_request",
2636 "of_table_mod",
2637 "of_set_config",
2638 "of_experimenter",
2639 "of_experimenter_stats_request",
2640 "of_group_desc_stats_request",
2641 "of_group_features_stats_request",
2642 "of_role_request"]
2643
2644 gen_message_handler_templates(out, msgs=msg_list)
2645
2646 out.write("""
2647#endif
2648""")
2649
2650def gen_jump_table_template(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002651 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002652 unhandled="unhandled_message"):
2653 """
2654 Generate a template for a jump table.
2655 @param out The file to which to write the functions
2656 """
2657 out.write("""
2658/*
2659 * Simple jump table definition for message handling
2660 */
2661typedef int (*msg_handler_f)(%(cxn_type)s cxn, of_object_t *obj);
2662typedef msg_handler_f msg_jump_table_t[OF_MESSAGE_OBJECT_COUNT];
2663
2664/* Jump table template for message objects */
2665extern msg_jump_table_t jump_table;
2666
2667/* C-code template */
2668msg_jump_table_t jump_table = {
2669 %(unhandled)s, /* OF_OBJECT; place holder for generic object */
2670""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2671 count = 0
2672 fn_name = unhandled
2673 for cls in of_g.ordered_messages:
2674 comma = ","
2675 count += 1
2676 if count == len(of_g.ordered_messages):
2677 comma = " "
2678 if not all_unhandled:
2679 fn_name = "%s_handler" % cls[3:]
2680 out.write(" %s%s /* %s */\n" % (fn_name, comma, enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002681
Rich Lanea06d0c32013-03-25 08:52:03 -07002682 out.write("};\n")
2683
2684def gen_message_switch_stmt_tmeplate(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002685 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002686 unhandled="unhandled_message"):
2687 out.write("""
2688/*
2689 * Simple switch statement for message handling
2690 */
2691
2692 switch (obj->object_id):
2693""")
2694 fn_name = unhandled
2695 for cls in of_g.ordered_messages:
2696 if not all_unhandled:
2697 fn_name = "%s_handler" % cls[3:]
2698 out.write("""
2699 case %(enum)s:
2700 rv = %(fn_name)s(cxn, obj);
2701 break;
2702""" % dict(fn_name=fn_name, cls=cls, enum=enum_name(cls)))
2703 out.write("""
2704 default:
2705 rv = LS_ERROR_PARAM;
2706 break;
2707 }
2708
2709 TRACE("Handled msg %p with rv %d (%s)", obj, rv, ls_error_strings[rv]);
2710
2711 return rv;
2712""")
2713
2714
2715def gen_message_handler_templates(out=sys.stdout, cxn_type="ls_cxn_handle_t",
2716 unhandled="unhandled_message", msgs=None):
2717 gen_jump_table_template(out, False, cxn_type)
2718 out.write("""
2719/**
2720 * Function for unhandled message
2721 */
2722static int
2723unhandled_message(%(cxn_type)s cxn, of_object_t *obj)
2724{
2725 (void)cxn;
2726 (void)obj;
2727 TRACE("Unhandled message %%p. Object id %%d", obj, obj->object_id);
2728
2729 return LS_ERROR_UNAVAIL;
2730}
2731""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2732
2733 if not msgs:
2734 msgs = of_g.ordered_messages
2735 for cls in msgs:
2736 out.write("""
2737/**
2738 * Handle a %(s_cls)s message
2739 * @param cxn Connection handler for the owning connection
2740 * @param _obj Generic type object for the message to be coerced
2741 * @returns Error code
2742 */
2743
2744static int
2745%(s_cls)s_handler(%(cxn_type)s cxn, of_object_t *_obj)
2746{
2747 %(cls)s_t *obj;
2748
2749 TRACE("Handling %(cls)s message: %%p.", obj);
2750 obj = (%(cls)s_t *)_obj;
2751
2752 /* Handle object of type %(cls)s_t */
2753
2754 return LS_ERROR_NONE;
2755}
2756""" % dict(s_cls=cls[3:], cls=cls, cxn_type=cxn_type))
2757 gen_message_switch_stmt_tmeplate(out, False, cxn_type)