blob: a0fec7e6973c4984c21158be9ac77cf136b22b6b [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# @brief C code generation for LOXI type related maps
30#
31
32import of_g
33import sys
34from generic_utils import *
Rich Lanea06d0c32013-03-25 08:52:03 -070035import loxi_front_end.type_maps as type_maps
36
37
38# Some number larger than small type values, but less then
39# reserved values like 0xffff
40max_type_value = 1000
41
42def gen_object_id_to_type(out):
43 out.write("""
44/**
45 * Map from object ID to primary wire type
46 *
47 * For messages, this is the header type; in particular for stats, this is
48 * the common stats request/response type. For per-stats types, use the
49 * stats type map. For things like actions, instructions or queue-props,
50 * this gives the "sub type".
51 */
52""")
53 for version in of_g.of_version_range:
Rich Laneb157b0f2013-03-27 13:55:28 -070054 out.write("static const int\nof_object_to_type_map_v%d[OF_OBJECT_COUNT] = {\n"
Rich Lanea06d0c32013-03-25 08:52:03 -070055 %version)
56 out.write(" -1, /* of_object, not a valid specific type */\n")
57 for j, cls in enumerate(of_g.all_class_order):
58 comma = ""
59 if j < len(of_g.all_class_order) - 1: # Avoid ultimate comma
60 comma = ","
61
62 if cls in type_maps.stats_reply_list:
Andreas Wundsam53256162013-05-02 14:05:53 -070063 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070064 (type_maps.type_val[("of_stats_reply", version)],
65 comma, cls))
66 elif cls in type_maps.stats_request_list:
Andreas Wundsam53256162013-05-02 14:05:53 -070067 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070068 (type_maps.type_val[("of_stats_request", version)],
69 comma, cls))
70 elif cls in type_maps.flow_mod_list:
Andreas Wundsam53256162013-05-02 14:05:53 -070071 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070072 (type_maps.type_val[("of_flow_mod", version)],
73 comma, cls))
74 elif (cls, version) in type_maps.type_val:
Andreas Wundsam53256162013-05-02 14:05:53 -070075 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070076 (type_maps.type_val[(cls, version)], comma, cls))
77 elif type_maps.message_is_extension(cls, version):
Andreas Wundsam53256162013-05-02 14:05:53 -070078 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070079 (type_maps.type_val[("of_experimenter", version)],
80 comma, cls))
81 elif type_maps.action_is_extension(cls, version):
Andreas Wundsam53256162013-05-02 14:05:53 -070082 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070083 (type_maps.type_val[("of_action_experimenter",
84 version)],
85 comma, cls))
86 elif type_maps.action_id_is_extension(cls, version):
Andreas Wundsam53256162013-05-02 14:05:53 -070087 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070088 (type_maps.type_val[("of_action_id_experimenter",
89 version)],
90 comma, cls))
91 elif type_maps.instruction_is_extension(cls, version):
Andreas Wundsam53256162013-05-02 14:05:53 -070092 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070093 (type_maps.type_val[("of_instruction_experimenter",
94 version)],
95 comma, cls))
96 elif type_maps.queue_prop_is_extension(cls, version):
Andreas Wundsam53256162013-05-02 14:05:53 -070097 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -070098 (type_maps.type_val[("of_queue_prop_experimenter",
99 version)],
100 comma, cls))
101 elif type_maps.table_feature_prop_is_extension(cls, version):
Andreas Wundsam53256162013-05-02 14:05:53 -0700102 out.write(" %d%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -0700103 (type_maps.type_val[("of_table_feature_prop_experimenter",
104 version)],
105 comma, cls))
106 else:
107 out.write(" -1%s /* %s (invalid) */\n" % (comma, cls))
108 out.write("};\n\n")
109
110 out.write("""
111/**
112 * Unified map, indexed by wire version which is 1-based.
113 */
Rich Laneb157b0f2013-03-27 13:55:28 -0700114const int *const of_object_to_type_map[OF_VERSION_ARRAY_MAX] = {
Rich Lanea06d0c32013-03-25 08:52:03 -0700115 NULL,
116""")
117 for version in of_g.of_version_range:
118 out.write(" of_object_to_type_map_v%d,\n" % version)
119 out.write("""
120};
121""")
122
123def gen_object_id_to_extension_data(out):
124 out.write("""
125/**
126 * Extension data.
127 * @fixme There must be a better way to represent this data
128 */
129""")
130 for version in of_g.of_version_range:
131 out.write("""
Rich Laneb157b0f2013-03-27 13:55:28 -0700132static const of_experimenter_data_t
Rich Lanea06d0c32013-03-25 08:52:03 -0700133of_object_to_extension_data_v%d[OF_OBJECT_COUNT] = {
134""" % version)
135 out.write(" {0, 0, 0}, /* of_object, not a valid specific type */\n")
136 for j, cls in enumerate(of_g.all_class_order):
137 comma = ""
138 if j < len(of_g.all_class_order) - 1: # Avoid ultimate comma
139 comma = ","
140
141 if type_maps.class_is_extension(cls, version):
142 exp_name = type_maps.extension_to_experimenter_macro_name(cls)
143 subtype = type_maps.extension_to_subtype(cls, version)
Andreas Wundsam53256162013-05-02 14:05:53 -0700144 out.write(" {1, %s, %d}%s /* %s */\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -0700145 (exp_name, subtype, comma, cls))
146 else:
147 out.write(" {0, 0, 0}%s /* %s (non-extension) */\n" %
148 (comma, cls))
149 out.write("};\n\n")
150
151 out.write("""
152/**
153 * Unified map, indexed by wire version which is 1-based.
154 */
Rich Laneb157b0f2013-03-27 13:55:28 -0700155const of_experimenter_data_t *const of_object_to_extension_data[OF_VERSION_ARRAY_MAX] = {
Rich Lanea06d0c32013-03-25 08:52:03 -0700156 NULL,
157""")
158 for version in of_g.of_version_range:
159 out.write(" of_object_to_extension_data_v%d,\n" % version)
160 out.write("""
161};
162""")
163
164def gen_type_to_object_id(out, type_str, prefix, template,
165 value_array, max_val):
166 """
167 Generate C maps from various message class groups to object ids
168
169 For each version, create an array mapping the type info to the
170 object ID. Then define an array containing those pointers.
171 """
172
173 # Create unified arrays and get length
174 arr_len = type_maps.type_array_len(value_array, max_val)
175 all_ars = []
176 for version, val_dict in value_array.items(): # Per version dict
177 ar = type_maps.dict_to_array(val_dict, max_val, type_maps.invalid_type)
178 all_ars.append(ar)
179
180 len_name = "%s_ITEM_COUNT" % prefix
181
182 for i, ar in enumerate(all_ars):
183 version = i + 1
Rich Laneb157b0f2013-03-27 13:55:28 -0700184 out.write("static const of_object_id_t\nof_%s_v%d[%s] = {\n" %
Rich Lanea06d0c32013-03-25 08:52:03 -0700185 (type_str, version, len_name))
186 for i in range(arr_len):
187 comma = ""
188 if i < arr_len - 1: # Avoid ultimate comma
189 comma = ","
190
191 # Per-version length check
192 if i < len(ar):
193 v = ar[i]
194 else:
195 v = type_maps.invalid_type
196
197 if v == type_maps.invalid_type:
198 out.write(" %-30s /* %d (Invalid) */\n" %
199 ("OF_OBJECT_INVALID" + comma, i))
200 else:
201 name = (template % v.upper()) + comma
202 out.write(" %-30s /* %d */\n" % (name, i))
203 out.write("};\n")
204
205 out.write("""
206/**
207 * Maps from %(c_name)s wire type values to LOCI object ids
208 *
209 * Indexed by wire version which is 1-based.
210 */
211
Rich Laneb157b0f2013-03-27 13:55:28 -0700212const of_object_id_t *const of_%(name)s[OF_VERSION_ARRAY_MAX] = {
Rich Lanea06d0c32013-03-25 08:52:03 -0700213 NULL,
214""" % dict(name=type_str, c_name=prefix.lower()))
215 for version in of_g.of_version_range:
216 out.write(" of_%(name)s_v%(version)d,\n" % dict(name=type_str,
217 version=version))
218 out.write("""
219};
220
Andreas Wundsam53256162013-05-02 14:05:53 -0700221""" % dict(name=type_str, u_name=type_str.upper(),
Rich Lanea06d0c32013-03-25 08:52:03 -0700222 max_val=max_val, c_name=prefix.lower()))
223
224def gen_type_maps(out):
225 """
226 Generate various type maps
227 @param out The file handle to write to
228 """
229
230 out.write("#include <loci/loci.h>\n\n")
231
232 # Generate maps from wire type values to object IDs
233 gen_type_to_object_id(out, "action_type_to_id", "OF_ACTION",
234 "OF_ACTION_%s", type_maps.action_types,
235 max_type_value)
236 gen_type_to_object_id(out, "action_id_type_to_id", "OF_ACTION_ID",
237 "OF_ACTION_ID_%s", type_maps.action_id_types,
238 max_type_value)
239 gen_type_to_object_id(out, "instruction_type_to_id", "OF_INSTRUCTION",
Andreas Wundsam53256162013-05-02 14:05:53 -0700240 "OF_INSTRUCTION_%s", type_maps.instruction_types,
Rich Lanea06d0c32013-03-25 08:52:03 -0700241 max_type_value)
242 gen_type_to_object_id(out, "queue_prop_type_to_id", "OF_QUEUE_PROP",
243 "OF_QUEUE_PROP_%s", type_maps.queue_prop_types,
244 max_type_value)
245 gen_type_to_object_id(out, "table_feature_prop_type_to_id",
246 "OF_TABLE_FEATURE_PROP",
247 "OF_TABLE_FEATURE_PROP_%s",
248 type_maps.table_feature_prop_types,
249 max_type_value)
250 gen_type_to_object_id(out, "meter_band_type_to_id", "OF_METER_BAND",
251 "OF_METER_BAND_%s", type_maps.meter_band_types,
252 max_type_value)
253 gen_type_to_object_id(out, "hello_elem_type_to_id", "OF_HELLO_ELEM",
254 "OF_HELLO_ELEM_%s", type_maps.hello_elem_types,
255 max_type_value)
256
257 # FIXME: Multipart re-organization
258 gen_type_to_object_id(out, "stats_request_type_to_id", "OF_STATS_REQUEST",
259 "OF_%s_STATS_REQUEST", type_maps.stats_types,
260 max_type_value)
261 gen_type_to_object_id(out, "stats_reply_type_to_id", "OF_STATS_REPLY",
262 "OF_%s_STATS_REPLY", type_maps.stats_types,
263 max_type_value)
264 gen_type_to_object_id(out, "flow_mod_type_to_id", "OF_FLOW_MOD",
265 "OF_FLOW_%s", type_maps.flow_mod_types,
266 max_type_value)
267 gen_type_to_object_id(out, "oxm_type_to_id", "OF_OXM",
268 "OF_OXM_%s", type_maps.oxm_types, max_type_value)
269 gen_type_to_object_id(out, "message_type_to_id", "OF_MESSAGE",
270 "OF_%s", type_maps.message_types, max_type_value)
271
272 gen_object_id_to_type(out)
273 gen_object_id_to_extension_data(out)
274 # Don't need array mapping ID to stats types right now; handled directly
275 # gen_object_id_to_stats_type(out)
276
277
278def gen_type_to_obj_map_functions(out):
279 """
280 Generate the templated static inline type map functions
281 @param out The file handle to write to
282 """
283
284 ################################################################
285 # Generate all type-to-object-ID maps in a common way
286 ################################################################
287 map_template = """
288/**
289 * %(name)s wire type to object ID array.
290 * Treat as private; use function accessor below
291 */
292
Rich Laneb157b0f2013-03-27 13:55:28 -0700293extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
Rich Lanea06d0c32013-03-25 08:52:03 -0700294
295#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
296
297/**
298 * Map an %(name)s wire value to an OF object
299 * @param %(name)s The %(name)s type wire value
300 * @param version The version associated with the check
301 * @return The %(name)s OF object type
302 * @return OF_OBJECT_INVALID if type does not map to an object
Andreas Wundsam53256162013-05-02 14:05:53 -0700303 *
Rich Lanea06d0c32013-03-25 08:52:03 -0700304 */
305static inline of_object_id_t
Andreas Wundsam53256162013-05-02 14:05:53 -0700306of_%(name)s_to_object_id(int %(name)s, of_version_t version)
Rich Lanea06d0c32013-03-25 08:52:03 -0700307{
308 if (!OF_VERSION_OKAY(version)) {
309 return OF_OBJECT_INVALID;
310 }
311 if (%(name)s < 0 || %(name)s >= OF_%(u_name)s_ITEM_COUNT) {
312 return OF_OBJECT_INVALID;
313 }
314
315 return of_%(name)s_type_to_id[version][%(name)s];
316}
317"""
318 map_with_experimenter_template = """
319/**
320 * %(name)s wire type to object ID array.
321 * Treat as private; use function accessor below
322 */
323
Rich Laneb157b0f2013-03-27 13:55:28 -0700324extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
Rich Lanea06d0c32013-03-25 08:52:03 -0700325
326#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
327
328/**
329 * Map an %(name)s wire value to an OF object
330 * @param %(name)s The %(name)s type wire value
331 * @param version The version associated with the check
332 * @return The %(name)s OF object type
333 * @return OF_OBJECT_INVALID if type does not map to an object
Andreas Wundsam53256162013-05-02 14:05:53 -0700334 *
Rich Lanea06d0c32013-03-25 08:52:03 -0700335 */
336static inline of_object_id_t
Andreas Wundsam53256162013-05-02 14:05:53 -0700337of_%(name)s_to_object_id(int %(name)s, of_version_t version)
Rich Lanea06d0c32013-03-25 08:52:03 -0700338{
339 if (!OF_VERSION_OKAY(version)) {
340 return OF_OBJECT_INVALID;
341 }
342 if (%(name)s == OF_EXPERIMENTER_TYPE) {
343 return OF_%(u_name)s_EXPERIMENTER;
344 }
345 if (%(name)s < 0 || %(name)s >= OF_%(u_name)s_ITEM_COUNT) {
346 return OF_OBJECT_INVALID;
347 }
348
349 return of_%(name)s_type_to_id[version][%(name)s];
350}
351"""
352
353 stats_template = """
354/**
355 * %(name)s wire type to object ID array.
356 * Treat as private; use function accessor below
357 */
358
Rich Laneb157b0f2013-03-27 13:55:28 -0700359extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
Rich Lanea06d0c32013-03-25 08:52:03 -0700360
361#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
362
363/**
364 * Map an %(name)s wire value to an OF object
365 * @param %(name)s The %(name)s type wire value
366 * @param version The version associated with the check
367 * @return The %(name)s OF object type
368 * @return OF_OBJECT_INVALID if type does not map to an object
Andreas Wundsam53256162013-05-02 14:05:53 -0700369 *
Rich Lanea06d0c32013-03-25 08:52:03 -0700370 */
371static inline of_object_id_t
Andreas Wundsam53256162013-05-02 14:05:53 -0700372of_%(name)s_to_object_id(int %(name)s, of_version_t version)
Rich Lanea06d0c32013-03-25 08:52:03 -0700373{
374 if (!OF_VERSION_OKAY(version)) {
375 return OF_OBJECT_INVALID;
376 }
377 if (%(name)s == OF_EXPERIMENTER_TYPE) {
378 return OF_EXPERIMENTER_%(u_name)s;
379 }
380 if (%(name)s < 0 || %(name)s >= OF_%(u_name)s_ITEM_COUNT) {
381 return OF_OBJECT_INVALID;
382 }
383
384 return of_%(name)s_type_to_id[version][%(name)s];
385}
386"""
387 # Experimenter mapping functions
388 # Currently we support very few candidates, so we just do a
389 # list of if/elses
390 experimenter_function = """
391/**
392 * @brief Map a message known to be an exp msg to the proper object
393 *
394 * Assume that the message is a vendor/experimenter message. Determine
395 * the specific object type for the message.
396 * @param msg An OF message object (uint8_t *)
397 * @param length The number of bytes in the message (for error checking)
398 * @param version Version of message
399 * @returns object ID of specific type if recognized or OF_EXPERIMENTER if not
400 *
401 * @todo put OF_EXPERIMENTER_<name> in loci_base.h
402 */
403
404static inline of_object_id_t
405of_message_experimenter_to_object_id(of_message_t msg, of_version_t version) {
406 uint32_t experimenter_id;
407 uint32_t subtype;
408
409 /* Extract experimenter and subtype value; look for match from type maps */
410 experimenter_id = of_message_experimenter_id_get(msg);
411 subtype = of_message_experimenter_subtype_get(msg);
412
413 /* Do a simple if/else search for the ver, experimenter and subtype */
414"""
415 first = True
416 for version, experimenter_lists in type_maps.extension_message_subtype.items():
417 for exp, subtypes in experimenter_lists.items():
418 experimenter_function += """
Andreas Wundsam53256162013-05-02 14:05:53 -0700419 if ((experimenter_id == OF_EXPERIMENTER_ID_%(exp_name)s) &&
Rich Lanea06d0c32013-03-25 08:52:03 -0700420 (version == %(ver_name)s)) {
421""" % dict(exp_name=exp.upper(), ver_name=of_g.wire_ver_map[version])
422 for ext_msg, subtype in subtypes.items():
423 experimenter_function += """
424 if (subtype == %(subtype)s) {
425 return %(ext_msg)s;
426 }
427""" % dict(subtype=subtype, ext_msg=ext_msg.upper())
428 experimenter_function += """
429 }
430"""
431 experimenter_function += """
432 return OF_EXPERIMENTER;
433}
434"""
435
436 # Message need different handling
437 msg_template = """
438/**
439 * %(name)s wire type to object ID array.
440 * Treat as private; use function accessor below
441 */
442
Rich Laneb157b0f2013-03-27 13:55:28 -0700443extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
Rich Lanea06d0c32013-03-25 08:52:03 -0700444
445#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
446
447/**
448 * Extract the type info from the message and determine its object type
449 * @param msg An OF message object (uint8_t *)
450 * @param length The number of bytes in the message (for error checking)
451 * @returns object ID or OF_OBJECT_INVALID if parse error
452 */
453
454static inline of_object_id_t
455of_message_to_object_id(of_message_t msg, int length) {
456 uint8_t type;
457 of_version_t ver;
458 of_object_id_t obj_id;
459 uint16_t stats_type;
460 uint8_t flow_mod_cmd;
461
462 if (length < OF_MESSAGE_MIN_LENGTH) {
463 return OF_OBJECT_INVALID;
464 }
465 type = of_message_type_get(msg);
466 ver = of_message_version_get(msg);
467 if (!OF_VERSION_OKAY(ver)) {
468 return OF_OBJECT_INVALID;
469 }
470
471 if (type >= OF_MESSAGE_ITEM_COUNT) {
472 return OF_OBJECT_INVALID;
473 }
474
475 obj_id = of_message_type_to_id[ver][type];
476
477 /* Remap to specific message if known */
478 if (obj_id == OF_EXPERIMENTER) {
479 if (length < OF_MESSAGE_EXPERIMENTER_MIN_LENGTH) {
480 return OF_OBJECT_INVALID;
481 }
482 return of_message_experimenter_to_object_id(msg, ver);
483 }
484
485 /* Remap to add/delete/strict version */
486 if (obj_id == OF_FLOW_MOD) {
487 if (length < OF_MESSAGE_MIN_FLOW_MOD_LENGTH(ver)) {
488 return OF_OBJECT_INVALID;
489 }
490 flow_mod_cmd = of_message_flow_mod_command_get(msg, ver);
491 obj_id = of_flow_mod_to_object_id(flow_mod_cmd, ver);
492 }
493
494 if ((obj_id == OF_STATS_REQUEST) || (obj_id == OF_STATS_REPLY)) {
495 if (length < OF_MESSAGE_MIN_STATS_LENGTH) {
496 return OF_OBJECT_INVALID;
497 }
498 stats_type = of_message_stats_type_get(msg);
499 if (obj_id == OF_STATS_REQUEST) {
500 obj_id = of_stats_request_to_object_id(stats_type, ver);
501 } else {
502 obj_id = of_stats_reply_to_object_id(stats_type, ver);
503 }
504 }
505
506 return obj_id;
507}
508"""
509
510 # Action types array gen
511 ar_len = type_maps.type_array_len(type_maps.action_types, max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700512 out.write(map_with_experimenter_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700513 dict(name="action", u_name="ACTION", ar_len=ar_len))
514
515 # Action ID types array gen
516 ar_len = type_maps.type_array_len(type_maps.action_id_types, max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700517 out.write(map_with_experimenter_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700518 dict(name="action_id", u_name="ACTION_ID", ar_len=ar_len))
519
520 # Instruction types array gen
521 ar_len = type_maps.type_array_len(type_maps.instruction_types,
522 max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700523 out.write(map_with_experimenter_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700524 dict(name="instruction", u_name="INSTRUCTION", ar_len=ar_len))
525
526 # Queue prop types array gen
527 ar_len = type_maps.type_array_len(type_maps.queue_prop_types,
528 max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700529 out.write(map_with_experimenter_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700530 dict(name="queue_prop", u_name="QUEUE_PROP", ar_len=ar_len))
531
532 # Table feature prop types array gen
533 ar_len = type_maps.type_array_len(type_maps.table_feature_prop_types,
534 max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700535 out.write(map_with_experimenter_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700536 dict(name="table_feature_prop", u_name="TABLE_FEATURE_PROP",
537 ar_len=ar_len))
538
539 # Meter band types array gen
540 ar_len = type_maps.type_array_len(type_maps.meter_band_types,
541 max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700542 out.write(map_with_experimenter_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700543 dict(name="meter_band", u_name="METER_BAND", ar_len=ar_len))
544
545 # Hello elem types array gen
546 ar_len = type_maps.type_array_len(type_maps.hello_elem_types,
547 max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700548 out.write(map_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700549 dict(name="hello_elem", u_name="HELLO_ELEM", ar_len=ar_len))
550
551 # Stats types array gen
552 ar_len = type_maps.type_array_len(type_maps.stats_types,
553 max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700554 out.write(stats_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700555 dict(name="stats_reply", u_name="STATS_REPLY", ar_len=ar_len))
Andreas Wundsam53256162013-05-02 14:05:53 -0700556 out.write(stats_template %
557 dict(name="stats_request", u_name="STATS_REQUEST",
Rich Lanea06d0c32013-03-25 08:52:03 -0700558 ar_len=ar_len))
559
560 ar_len = type_maps.type_array_len(type_maps.flow_mod_types, max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700561 out.write(map_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700562 dict(name="flow_mod", u_name="FLOW_MOD", ar_len=ar_len))
563
564 ar_len = type_maps.type_array_len(type_maps.oxm_types, max_type_value)
565 out.write("""
566/* NOTE: We could optimize the OXM and only generate OF 1.2 versions. */
567""")
Andreas Wundsam53256162013-05-02 14:05:53 -0700568 out.write(map_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700569 dict(name="oxm", u_name="OXM", ar_len=ar_len))
570
571 out.write(experimenter_function)
572 # Must follow stats reply/request
573 ar_len = type_maps.type_array_len(type_maps.message_types, max_type_value)
Andreas Wundsam53256162013-05-02 14:05:53 -0700574 out.write(msg_template %
Rich Lanea06d0c32013-03-25 08:52:03 -0700575 dict(name="message", u_name="MESSAGE", ar_len=ar_len))
576
577def gen_obj_to_type_map_functions(out):
578 """
579 Generate the static line maps from object IDs to types
580 @param out The file handle to write to
581 """
582
583 ################################################################
584 # Generate object ID to primary type map
585 ################################################################
586
587 out.write("""
Rich Laneb157b0f2013-03-27 13:55:28 -0700588extern const int *const of_object_to_type_map[OF_VERSION_ARRAY_MAX];
Rich Lanea06d0c32013-03-25 08:52:03 -0700589
590/**
591 * Map an object ID to its primary wire type value
592 * @param id An object ID
593 * @return For message objects, the type value in the OpenFlow header
594 * @return For non-message objects such as actions, instructions, OXMs
595 * returns the type value that appears in the respective sub-header
596 * @return -1 For improper version or out of bounds input
597 *
598 * NOTE that for stats request/reply, returns the header type, not the
599 * sub-type
600 *
601 * Also, note that the value is returned as a signed integer. So -1 is
602 * an error code, while 0xffff is the usual "experimenter" code.
603 */
604static inline int
605of_object_to_wire_type(of_object_id_t id, of_version_t version)
606{
607 if (!OF_VERSION_OKAY(version)) {
608 return -1;
609 }
610 if (id < 0 || id >= OF_OBJECT_COUNT) {
611 return -1;
612 }
613 return of_object_to_type_map[version][id];
614}
615
616""")
617
618 # Now for experimenter ids
619 out.write("""
620/**
621 * Map from object ID to a triple, (is_extension, experimenter id, subtype)
622 */
623""")
624 out.write("""
625typedef struct of_experimenter_data_s {
626 int is_extension; /* Boolean indication that this is an extension */
627 uint32_t experimenter_id;
628 uint32_t subtype;
629} of_experimenter_data_t;
630
631""")
632
633 out.write("""
Rich Laneb157b0f2013-03-27 13:55:28 -0700634extern const of_experimenter_data_t *const of_object_to_extension_data[OF_VERSION_ARRAY_MAX];
Rich Lanea06d0c32013-03-25 08:52:03 -0700635
636/**
637 * Map from the object ID of an extension to the experimenter ID
638 */
639static inline uint32_t
640of_extension_to_experimenter_id(of_object_id_t obj_id, of_version_t ver)
641{
642 if (obj_id < 0 || obj_id > OF_OBJECT_COUNT) {
643 return (uint32_t) -1;
644 }
645 /* @fixme: Verify ver? */
646 return of_object_to_extension_data[ver][obj_id].experimenter_id;
647}
648
649/**
650 * Map from the object ID of an extension to the experimenter subtype
651 */
652static inline uint32_t
653of_extension_to_experimenter_subtype(of_object_id_t obj_id, of_version_t ver)
654{
655 if (obj_id < 0 || obj_id > OF_OBJECT_COUNT) {
656 return (uint32_t) -1;
657 }
658 /* @fixme: Verify ver? */
659 return of_object_to_extension_data[ver][obj_id].subtype;
660}
661
662/**
663 * Boolean function indicating the the given object ID/version
664 * is recognized as a supported (decode-able) extension.
665 */
666static inline int
667of_object_id_is_extension(of_object_id_t obj_id, of_version_t ver)
668{
669 if (obj_id < 0 || obj_id > OF_OBJECT_COUNT) {
670 return (uint32_t) -1;
671 }
672 /* @fixme: Verify ver? */
673 return of_object_to_extension_data[ver][obj_id].is_extension;
674}
675""")
676
677 ################################################################
678 # Generate object ID to the stats sub-type map
679 ################################################################
680
681 out.write("""
682/**
683 * Map an object ID to a stats type
684 * @param id An object ID
685 * @return The wire value for the stats type
686 * @return -1 if not supported for this version
687 * @return -1 if id is not a specific stats type ID
688 *
689 * Note that the value is returned as a signed integer. So -1 is
690 * an error code, while 0xffff is the usual "experimenter" code.
691 */
692
693static inline int
694of_object_to_stats_type(of_object_id_t id, of_version_t version)
695{
696 if (!OF_VERSION_OKAY(version)) {
697 return -1;
698 }
699 switch (id) {
700""")
701 # Assumes 1.2 contains all stats types and type values are
702 # the same across all versions
703 stats_names = dict()
704 for ver in of_g.of_version_range:
705 for name, value in type_maps.stats_types[ver].items():
706 if name in stats_names and (not value == stats_names[name]):
707 print "ERROR stats type differ violating assumption"
708 sys.exit(1)
709 stats_names[name] = value
710
711 for name, value in stats_names.items():
712 out.write(" case OF_%s_STATS_REPLY:\n" % name.upper())
713 out.write(" case OF_%s_STATS_REQUEST:\n" % name.upper())
714 for version in of_g.of_version_range:
715 if not name in type_maps.stats_types[version]:
716 out.write(" if (version == %s) break;\n" %
717 of_g.of_version_wire2name[version])
718 out.write(" return %d;\n" % value)
719 out.write("""
720 default:
721 break;
722 }
723 return -1; /* Not recognized as stats type object for this version */
724}
725""")
726
727 ################################################################
728 # Generate object ID to the flow mod sub-type map
729 ################################################################
730
731 out.write("""
732/**
733 * Map an object ID to a flow-mod command value
734 * @param id An object ID
735 * @return The wire value for the flow-mod command
736 * @return -1 if not supported for this version
737 * @return -1 if id is not a specific stats type ID
738 *
739 * Note that the value is returned as a signed integer. So -1 is
740 * an error code, while 0xffff is the usual "experimenter" code.
741 */
742
743static inline int
744of_object_to_flow_mod_command(of_object_id_t id, of_version_t version)
745{
746 if (!OF_VERSION_OKAY(version)) {
747 return -1;
748 }
749 switch (id) {
750""")
751 # Assumes 1.2 contains all stats types and type values are
752 # the same across all versions
753 flow_mod_names = dict()
754 for ver in of_g.of_version_range:
755 for name, value in type_maps.flow_mod_types[ver].items():
756 if name in flow_mod_names and \
757 (not value == flow_mod_names[name]):
758 print "ERROR flow mod command differ violating assumption"
759 sys.exit(1)
760 flow_mod_names[name] = value
761
762 for name, value in flow_mod_names.items():
763 out.write(" case OF_FLOW_%s:\n" % name.upper())
764 for version in of_g.of_version_range:
765 if not name in type_maps.flow_mod_types[version]:
766 out.write(" if (version == %s) break;\n" %
767 of_g.of_version_wire2name[version])
768 out.write(" return %d;\n" % value)
769 out.write("""
770 default:
771 break;
772 }
773 return -1; /* Not recognized as flow mod type object for this version */
774}
775
776""")
777
778def gen_type_maps_header(out):
779 """
780 Generate various header file declarations for type maps
781 @param out The file handle to write to
782 """
783
784 out.write("""
785/**
Andreas Wundsam53256162013-05-02 14:05:53 -0700786 * Generic experimenter type value. Applies to all except
Rich Lanea06d0c32013-03-25 08:52:03 -0700787 * top level message: Action, instruction, error, stats, queue_props, oxm
788 */
789#define OF_EXPERIMENTER_TYPE 0xffff
790""")
791 gen_type_to_obj_map_functions(out)
792 gen_obj_to_type_map_functions(out)
793
Rich Laneb157b0f2013-03-27 13:55:28 -0700794 out.write("extern const int *const of_object_fixed_len[OF_VERSION_ARRAY_MAX];\n")
Rich Lanef70be942013-07-18 13:33:14 -0700795 out.write("extern const int *const of_object_extra_len[OF_VERSION_ARRAY_MAX];\n")
Rich Lanea06d0c32013-03-25 08:52:03 -0700796
797 out.write("""
798/**
799 * Map a message in a wire buffer object to its OF object id.
800 * @param wbuf Pointer to a wire buffer object, populated with an OF message
801 * @returns The object ID of the message
802 * @returns OF_OBJECT_INVALID if unable to parse the message type
803 */
804
805static inline of_object_id_t
806of_wire_object_id_get(of_wire_buffer_t *wbuf)
807{
808 of_message_t msg;
809
810 msg = (of_message_t)WBUF_BUF(wbuf);
811 return of_message_to_object_id(msg, WBUF_CURRENT_BYTES(wbuf));
812}
813
814/**
815 * Use the type/length from the wire buffer and init the object
816 * @param obj The object being initialized
817 * @param base_object_id If > 0, this indicates the base object
818 * @param max_len If > 0, the max length to expect for the obj
819 * type for inheritance checking
820 * @return OF_ERROR_
821 *
822 * Used for inheritance type objects such as actions and OXMs
823 * The type is checked and if valid, the object is initialized.
824 * Then the length is taken from the buffer.
825 *
826 * Note that the object version must already be properly set.
827 */
828static inline int
829of_object_wire_init(of_object_t *obj, of_object_id_t base_object_id,
830 int max_len)
831{
832 if (obj->wire_type_get != NULL) {
833 of_object_id_t id;
834 obj->wire_type_get(obj, &id);
835 if (!of_wire_id_valid(id, base_object_id)) {
836 return OF_ERROR_PARSE;
837 }
838 obj->object_id = id;
839 /* Call the init function for this object type; do not push to wire */
840 of_object_init_map[id]((of_object_t *)(obj), obj->version, -1, 0);
841 }
842 if (obj->wire_length_get != NULL) {
843 int length;
844 obj->wire_length_get(obj, &length);
845 if (length < 0 || (max_len > 0 && length > max_len)) {
846 return OF_ERROR_PARSE;
847 }
848 obj->length = length;
849 } else {
850 /* @fixme Does this cover everything else? */
851 obj->length = of_object_fixed_len[obj->version][base_object_id];
852 }
853
854 return OF_ERROR_NONE;
855}
856
857""")
858
859 # Generate the function that sets the object type fields
860 out.write("""
861
862/**
863 * Map a message in a wire buffer object to its OF object id.
864 * @param wbuf Pointer to a wire buffer object, populated with an OF message
865 * @returns The object ID of the message
866 * @returns OF_OBJECT_INVALID if unable to parse the message type
867 *
868 * Version must be set in the buffer prior to calling this routine
869 */
870
871static inline int
872of_wire_message_object_id_set(of_wire_buffer_t *wbuf, of_object_id_t id)
873{
874 int type;
875 of_version_t ver;
876 of_message_t msg;
877
878 msg = (of_message_t)WBUF_BUF(wbuf);
879
880 ver = of_message_version_get(msg);
881
882 /* ASSERT(id is a message object) */
883
884 if ((type = of_object_to_wire_type(id, ver)) < 0) {
885 return OF_ERROR_PARAM;
886 }
887 of_message_type_set(msg, type);
888
889 if ((type = of_object_to_stats_type(id, ver)) >= 0) {
890 /* It's a stats obj */
891 of_message_stats_type_set(msg, type);
892 }
893 if ((type = of_object_to_flow_mod_command(id, ver)) >= 0) {
894 /* It's a flow mod obj */
895 of_message_flow_mod_command_set(msg, ver, type);
896 }
897 if (of_object_id_is_extension(id, ver)) {
898 uint32_t val32;
899
900 /* Set the experimenter and subtype codes */
901 val32 = of_extension_to_experimenter_id(id, ver);
902 of_message_experimenter_id_set(msg, val32);
903 val32 = of_extension_to_experimenter_subtype(id, ver);
904 of_message_experimenter_subtype_set(msg, val32);
905 }
906
907 return OF_ERROR_NONE;
908}
909""")
910
911def gen_type_data_header(out):
912
913 out.write("""
914/****************************************************************
915 *
916 * The following declarations are for type and length calculations.
917 * Implementations may be found in of_type_maps.c
918 *
919 ****************************************************************/
920/*
921 * Special case length functions for objects with
922 */
923""")
924 for ((cls, name), prev) in of_g.special_offsets.items():
925 s_cls = cls[3:] # take off of_
926 out.write("""
927/**
928 * Special length calculation for %(cls)s->%(name)s.
Andreas Wundsam53256162013-05-02 14:05:53 -0700929 * @param obj An object of type %(cls)s to check for
Rich Lanea06d0c32013-03-25 08:52:03 -0700930 * length of %(name)s
931 * @param bytes[out] Where to store the calculated length
932 *
933 * Preceding data member is %(prev)s.
934 */
935extern int of_length_%(s_cls)s_%(name)s_get(
936 %(cls)s_t *obj, int *bytes);
937
938/**
939 * Special offset calculation for %(cls)s->%(name)s.
Andreas Wundsam53256162013-05-02 14:05:53 -0700940 * @param obj An object of type %(cls)s to check for
Rich Lanea06d0c32013-03-25 08:52:03 -0700941 * length of %(name)s
942 * @param offset[out] Where to store the calculated length
943 *
944 * Preceding data member is %(prev)s.
945 */
946extern int of_offset_%(s_cls)s_%(name)s_get(
947 %(cls)s_t *obj, int *offset);
948""" % dict(cls=cls, s_cls=s_cls, name=name, prev=prev))
949
950# NOT NEEDED YET
951# # For non-message, variable length objects, give a fun that
952# # calculates the length
953# for cls in of_g.standard_class_order:
954# s_cls = cls[3:] # take off of_
955# if !type_is_var_len(cls, version):
956# continue
957# out.write("""
958# /**
959# * Special length calculation for variable length object %(cls)s
960# * @param obj An object of type %(cls)s whose length is being calculated
961# * @param bytes[out] Where to store the calculated length
962# *
963# * The assumption is that the length member of the object is not
964# * valid and the length needs to be calculated from other information
965# * such as the parent.
966# */
967# extern int of_length_%(s_cls)s_get(
968# %(cls)s_t *obj, int *bytes);
Andreas Wundsam53256162013-05-02 14:05:53 -0700969# """ % dict(cls=cls, s_cls=s_cls))
Rich Lanea06d0c32013-03-25 08:52:03 -0700970
971 out.write("""
972/****************************************************************
973 * Wire type/length functions.
974 ****************************************************************/
975
976extern void of_object_message_wire_length_get(of_object_t *obj, int *bytes);
977extern void of_object_message_wire_length_set(of_object_t *obj, int bytes);
978
979extern void of_oxm_wire_length_get(of_object_t *obj, int *bytes);
980extern void of_oxm_wire_length_set(of_object_t *obj, int bytes);
981extern void of_oxm_wire_object_id_get(of_object_t *obj, of_object_id_t *id);
982extern void of_oxm_wire_object_id_set(of_object_t *obj, of_object_id_t id);
983
984extern void of_tlv16_wire_length_get(of_object_t *obj, int *bytes);
985extern void of_tlv16_wire_length_set(of_object_t *obj, int bytes);
986
987extern void of_tlv16_wire_object_id_set(of_object_t *obj, of_object_id_t id);
988
989/* Wire length is uint16 at front of structure */
990extern void of_u16_len_wire_length_get(of_object_t *obj, int *bytes);
991extern void of_u16_len_wire_length_set(of_object_t *obj, int bytes);
992
993extern void of_action_wire_object_id_get(of_object_t *obj, of_object_id_t *id);
994extern void of_action_id_wire_object_id_get(of_object_t *obj, of_object_id_t *id);
Andreas Wundsam53256162013-05-02 14:05:53 -0700995extern void of_instruction_wire_object_id_get(of_object_t *obj,
Rich Lanea06d0c32013-03-25 08:52:03 -0700996 of_object_id_t *id);
Andreas Wundsam53256162013-05-02 14:05:53 -0700997extern void of_queue_prop_wire_object_id_get(of_object_t *obj,
Rich Lanea06d0c32013-03-25 08:52:03 -0700998 of_object_id_t *id);
Andreas Wundsam53256162013-05-02 14:05:53 -0700999extern void of_table_feature_prop_wire_object_id_get(of_object_t *obj,
Rich Lanea06d0c32013-03-25 08:52:03 -07001000 of_object_id_t *id);
Andreas Wundsam53256162013-05-02 14:05:53 -07001001extern void of_meter_band_wire_object_id_get(of_object_t *obj,
Rich Lanea06d0c32013-03-25 08:52:03 -07001002 of_object_id_t *id);
Andreas Wundsam53256162013-05-02 14:05:53 -07001003extern void of_hello_elem_wire_object_id_get(of_object_t *obj,
Rich Lanea06d0c32013-03-25 08:52:03 -07001004 of_object_id_t *id);
1005
Rich Lane47085722013-07-12 16:27:04 -07001006/* XXX Hardcoded to the OpenFlow Basic OXM class */
Rich Lanea06d0c32013-03-25 08:52:03 -07001007#define OF_OXM_MASKED_TYPE_GET(hdr) (((hdr) >> 8) & 0xff)
1008#define OF_OXM_MASKED_TYPE_SET(hdr, val) \\
Rich Lane47085722013-07-12 16:27:04 -07001009 (hdr) = ((hdr) & 0x000000ff) + 0x80000000 + (((val) & 0xff) << 8)
Rich Lanea06d0c32013-03-25 08:52:03 -07001010
Rich Lane47085722013-07-12 16:27:04 -07001011#define OF_OXM_LENGTH_GET(hdr) (((hdr) & 0xff) + 4)
Rich Lanea06d0c32013-03-25 08:52:03 -07001012#define OF_OXM_LENGTH_SET(hdr, val) \\
Rich Lane47085722013-07-12 16:27:04 -07001013 (hdr) = ((hdr) & 0xffffff00) + (((val) - 4) & 0xff)
Rich Lanea06d0c32013-03-25 08:52:03 -07001014
1015extern void of_packet_queue_wire_length_get(of_object_t *obj, int *bytes);
1016extern void of_packet_queue_wire_length_set(of_object_t *obj, int bytes);
1017
1018extern void of_list_meter_band_stats_wire_length_get(of_object_t *obj,
1019 int *bytes);
1020extern void of_meter_stats_wire_length_get(of_object_t *obj, int *bytes);
1021extern void of_meter_stats_wire_length_set(of_object_t *obj, int bytes);
1022extern int of_extension_object_wire_push(of_object_t *obj);
1023
1024""")
1025
1026
1027def gen_length_array(out):
1028 """
1029 Generate an array giving the lengths of all objects/versions
1030 @param out The file handle to which to write
1031 """
1032 out.write("""
1033/**
1034 * An array with the number of bytes in the fixed length part
1035 * of each OF object
1036 */
1037""")
1038
1039 for version in of_g.of_version_range:
1040 out.write("""
Rich Laneb157b0f2013-03-27 13:55:28 -07001041static const int\nof_object_fixed_len_v%d[OF_OBJECT_COUNT] = {
Rich Lanea06d0c32013-03-25 08:52:03 -07001042 -1, /* of_object is not instantiable */
1043""" % version)
1044 for i, cls in enumerate(of_g.all_class_order):
1045 comma = ","
1046 if i == len(of_g.all_class_order) - 1:
1047 comma = ""
1048 val = "-1" + comma
1049 if (cls, version) in of_g.base_length:
1050 val = str(of_g.base_length[(cls, version)]) + comma
1051 out.write(" %-5s /* %d: %s */\n" % (val, i + 1, cls))
1052 out.write("};\n")
1053
1054 out.write("""
1055/**
1056 * Unified map of fixed length part of each object
1057 */
Rich Laneb157b0f2013-03-27 13:55:28 -07001058const int *const of_object_fixed_len[OF_VERSION_ARRAY_MAX] = {
Rich Lanea06d0c32013-03-25 08:52:03 -07001059 NULL,
1060""")
1061 for version in of_g.of_version_range:
1062 out.write(" of_object_fixed_len_v%d,\n" % version)
1063 out.write("""
1064};
1065""")
1066
Andreas Wundsam53256162013-05-02 14:05:53 -07001067
Rich Lanef70be942013-07-18 13:33:14 -07001068def gen_extra_length_array(out):
1069 """
1070 Generate an array giving the extra lengths of all objects/versions
1071 @param out The file handle to which to write
1072 """
1073 out.write("""
1074/**
1075 * An array with the number of bytes in the extra length part
1076 * of each OF object
1077 */
1078""")
1079
1080 for version in of_g.of_version_range:
1081 out.write("""
1082static const int\nof_object_extra_len_v%d[OF_OBJECT_COUNT] = {
1083 -1, /* of_object is not instantiable */
1084""" % version)
1085 for i, cls in enumerate(of_g.all_class_order):
1086 comma = ","
1087 if i == len(of_g.all_class_order) - 1:
1088 comma = ""
1089 val = "-1" + comma
1090 if (cls, version) in of_g.base_length:
1091 val = str(of_g.extra_length.get((cls, version), 0)) + comma
1092 out.write(" %-5s /* %d: %s */\n" % (val, i + 1, cls))
1093 out.write("};\n")
1094
1095 out.write("""
1096/**
1097 * Unified map of extra length part of each object
1098 */
1099const int *const of_object_extra_len[OF_VERSION_ARRAY_MAX] = {
1100 NULL,
1101""")
1102 for version in of_g.of_version_range:
1103 out.write(" of_object_extra_len_v%d,\n" % version)
1104 out.write("""
1105};
1106""")
1107
1108
Rich Lanea06d0c32013-03-25 08:52:03 -07001109################################################################
1110################################################################
1111
1112# THIS IS PROBABLY NOT NEEDED AND MAY NOT BE CALLED CURRENTLY
1113def gen_object_id_to_stats_type(out):
1114 out.write("""
1115/**
1116 * Map from message object ID to stats type
1117 *
1118 * All message object IDs are mapped for simplicity
1119 */
1120""")
1121 for version in of_g.of_version_range:
Rich Laneb157b0f2013-03-27 13:55:28 -07001122 out.write("const int *of_object_to_stats_type_map_v%d = {\n" % (i+1))
Rich Lanea06d0c32013-03-25 08:52:03 -07001123 out.write(" -1, /* of_object (invalid) */\n");
1124 for cls in of_g.ordered_messages:
1125 name = cls[3:]
1126 name = name[:name.find("_stats")]
1127 if (((cls in type_maps.stats_reply_list) or
1128 (cls in type_maps.stats_request_list)) and
1129 name in type_maps.stats_types[i]):
1130 out.write(" %d, /* %s */\n" %
1131 (type_maps.stats_types[i][name], cls))
1132 else:
1133 out.write(" -1, /* %s (invalid) */\n" % cls)
1134 out.write("};\n\n")
1135
1136 out.write("""
1137/**
1138 * Unified map, indexed by wire version which is 1-based.
1139 */
Rich Laneb157b0f2013-03-27 13:55:28 -07001140const int *of_object_to_stats_type_map[OF_VERSION_ARRAY_MAX] = {
Rich Lanea06d0c32013-03-25 08:52:03 -07001141 NULL,
1142""")
1143 for version in of_g.of_version_range:
1144 out.write(" of_object_to_stats_type_map_v%d,\n" % version)
1145 out.write("""
1146};
1147""")
1148