1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0+
3
4# This is simply to aide in creating the entries in the order of the value of
5# the device-global NI signal/terminal constants defined in comedi.h
6import comedi_h
7import os, sys, re
8from csv_collection import CSVCollection
9
10
11def c_to_o(filename, prefix='\t\t\t\t\t   ni_routing/', suffix=' \\'):
12  if not filename.endswith('.c'):
13    return ''
14  return prefix + filename.rpartition('.c')[0] + '.o' + suffix
15
16
17def routedict_to_structinit_single(name, D, return_name=False):
18  Locals = dict()
19  lines = [
20    '\t.family = "{}",'.format(name),
21    '\t.register_values = {',
22    '\t\t/*',
23    '\t\t * destination = {',
24	  '\t\t *              source          = register value,',
25	  '\t\t *              ...',
26	  '\t\t * }',
27		'\t\t */',
28  ]
29  if (False):
30    # print table with index0:src, index1:dest
31    D0 = D # (src-> dest->reg_value)
32    #D1 : destD
33  else:
34    D0 = dict()
35    for src, destD in D.items():
36      for dest, val in destD.items():
37        D0.setdefault(dest, {})[src] = val
38
39
40  D0 = sorted(D0.items(), key=lambda i: eval(i[0], comedi_h.__dict__, Locals))
41
42  for D0_sig, D1_D in D0:
43    D1 = sorted(D1_D.items(), key=lambda i: eval(i[0], comedi_h.__dict__, Locals))
44
45    lines.append('\t\t[B({})] = {{'.format(D0_sig))
46    for D1_sig, value in D1:
47      if not re.match('[VIU]\([^)]*\)', value):
48        sys.stderr.write('Invalid register format: {}\n'.format(repr(value)))
49        sys.stderr.write(
50          'Register values should be formatted with V(),I(),or U()\n')
51        raise RuntimeError('Invalid register values format')
52      lines.append('\t\t\t[B({})]\t= {},'.format(D1_sig, value))
53    lines.append('\t\t},')
54  lines.append('\t},')
55
56  lines = '\n'.join(lines)
57  if return_name:
58    return N, lines
59  else:
60    return lines
61
62
63def routedict_to_routelist_single(name, D, indent=1):
64  Locals = dict()
65
66  indents = dict(
67    I0 = '\t'*(indent),
68    I1 = '\t'*(indent+1),
69    I2 = '\t'*(indent+2),
70    I3 = '\t'*(indent+3),
71    I4 = '\t'*(indent+4),
72  )
73
74  if (False):
75    # data is src -> dest-list
76    D0 = D
77    keyname = 'src'
78    valname = 'dest'
79  else:
80    # data is dest -> src-list
81    keyname = 'dest'
82    valname = 'src'
83    D0 = dict()
84    for src, destD in D.items():
85      for dest, val in destD.items():
86        D0.setdefault(dest, {})[src] = val
87
88  # Sort by order of device-global names (numerically)
89  D0 = sorted(D0.items(), key=lambda i: eval(i[0], comedi_h.__dict__, Locals))
90
91  lines = [ '{I0}.device = "{name}",\n'
92            '{I0}.routes = (struct ni_route_set[]){{'
93            .format(name=name, **indents) ]
94  for D0_sig, D1_D in D0:
95    D1 = [ k for k,v in D1_D.items() if v ]
96    D1.sort(key=lambda i: eval(i, comedi_h.__dict__, Locals))
97
98    lines.append('{I1}{{\n{I2}.{keyname} = {D0_sig},\n'
99                         '{I2}.{valname} = (int[]){{'
100                 .format(keyname=keyname, valname=valname, D0_sig=D0_sig, **indents)
101    )
102    for D1_sig in D1:
103      lines.append( '{I3}{D1_sig},'.format(D1_sig=D1_sig, **indents) )
104    lines.append( '{I3}0, /* Termination */'.format(**indents) )
105
106    lines.append('{I2}}}\n{I1}}},'.format(**indents))
107
108  lines.append('{I1}{{ /* Termination of list */\n{I2}.{keyname} = 0,\n{I1}}},'
109               .format(keyname=keyname, **indents))
110
111  lines.append('{I0}}},'.format(**indents))
112
113  return '\n'.join(lines)
114
115
116class DeviceRoutes(CSVCollection):
117  MKFILE_SEGMENTS = 'device-route.mk'
118  SET_C = 'ni_device_routes.c'
119  ITEMS_DIR = 'ni_device_routes'
120  EXTERN_H = 'all.h'
121  OUTPUT_DIR = 'c'
122
123  output_file_top = """\
124// SPDX-License-Identifier: GPL-2.0+
125/*
126 *  comedi/drivers/ni_routing/{filename}
127 *  List of valid routes for specific NI boards.
128 *
129 *  COMEDI - Linux Control and Measurement Device Interface
130 *  Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu>
131 *
132 *  This program is free software; you can redistribute it and/or modify
133 *  it under the terms of the GNU General Public License as published by
134 *  the Free Software Foundation; either version 2 of the License, or
135 *  (at your option) any later version.
136 *
137 *  This program is distributed in the hope that it will be useful,
138 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
139 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
140 *  GNU General Public License for more details.
141 */
142
143/*
144 * The contents of this file are generated using the tools in
145 * comedi/drivers/ni_routing/tools
146 *
147 * Please use those tools to help maintain the contents of this file.
148 */
149
150#include "ni_device_routes.h"
151#include "{extern_h}"\
152""".format(filename=SET_C, extern_h=os.path.join(ITEMS_DIR, EXTERN_H))
153
154  extern_header = """\
155/* SPDX-License-Identifier: GPL-2.0+ */
156/*
157 *  comedi/drivers/ni_routing/{filename}
158 *  List of valid routes for specific NI boards.
159 *
160 *  COMEDI - Linux Control and Measurement Device Interface
161 *  Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu>
162 *
163 *  This program is free software; you can redistribute it and/or modify
164 *  it under the terms of the GNU General Public License as published by
165 *  the Free Software Foundation; either version 2 of the License, or
166 *  (at your option) any later version.
167 *
168 *  This program is distributed in the hope that it will be useful,
169 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
170 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
171 *  GNU General Public License for more details.
172 */
173
174/*
175 * The contents of this file are generated using the tools in
176 * comedi/drivers/ni_routing/tools
177 *
178 * Please use those tools to help maintain the contents of this file.
179 */
180
181#ifndef _COMEDI_DRIVERS_NI_ROUTING_NI_DEVICE_ROUTES_EXTERN_H
182#define _COMEDI_DRIVERS_NI_ROUTING_NI_DEVICE_ROUTES_EXTERN_H
183
184#include "../ni_device_routes.h"
185
186{externs}
187
188#endif //_COMEDI_DRIVERS_NI_ROUTING_NI_DEVICE_ROUTES_EXTERN_H
189"""
190
191  single_output_file_top = """\
192// SPDX-License-Identifier: GPL-2.0+
193/*
194 *  comedi/drivers/ni_routing/{filename}
195 *  List of valid routes for specific NI boards.
196 *
197 *  COMEDI - Linux Control and Measurement Device Interface
198 *  Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu>
199 *
200 *  This program is free software; you can redistribute it and/or modify
201 *  it under the terms of the GNU General Public License as published by
202 *  the Free Software Foundation; either version 2 of the License, or
203 *  (at your option) any later version.
204 *
205 *  This program is distributed in the hope that it will be useful,
206 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
207 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
208 *  GNU General Public License for more details.
209 */
210
211/*
212 * The contents of this file are generated using the tools in
213 * comedi/drivers/ni_routing/tools
214 *
215 * Please use those tools to help maintain the contents of this file.
216 */
217
218#include "../ni_device_routes.h"
219#include "{extern_h}"
220
221struct ni_device_routes {table_name} = {{\
222"""
223
224  def __init__(self, pattern='csv/device_routes/*.csv'):
225    super(DeviceRoutes,self).__init__(pattern)
226
227  def to_listinit(self):
228    chunks = [ self.output_file_top,
229      '',
230      'struct ni_device_routes *const ni_device_routes_list[] = {'
231    ]
232    # put the sheets in lexical order of device numbers then bus
233    sheets = sorted(self.items(), key=lambda i : tuple(i[0].split('-')[::-1]) )
234
235    externs = []
236    objs = [c_to_o(self.SET_C)]
237
238    for sheet,D in sheets:
239      S = sheet.lower()
240      dev_table_name = 'ni_{}_device_routes'.format(S.replace('-','_'))
241      sheet_filename = os.path.join(self.ITEMS_DIR,'{}.c'.format(S))
242      externs.append('extern struct ni_device_routes {};'.format(dev_table_name))
243
244      chunks.append('\t&{},'.format(dev_table_name))
245
246      s_chunks = [
247        self.single_output_file_top.format(
248          filename    = sheet_filename,
249          table_name  = dev_table_name,
250          extern_h    = self.EXTERN_H,
251        ),
252        routedict_to_routelist_single(S, D),
253        '};',
254      ]
255
256      objs.append(c_to_o(sheet_filename))
257
258      with open(os.path.join(self.OUTPUT_DIR, sheet_filename), 'w') as f:
259        f.write('\n'.join(s_chunks))
260        f.write('\n')
261
262    with open(os.path.join(self.OUTPUT_DIR, self.MKFILE_SEGMENTS), 'w') as f:
263      f.write('# This is the segment that should be included in comedi/drivers/Makefile\n')
264      f.write('ni_routing-objs\t\t\t\t+= \\\n')
265      f.write('\n'.join(objs))
266      f.write('\n')
267
268    EXTERN_H = os.path.join(self.ITEMS_DIR, self.EXTERN_H)
269    with open(os.path.join(self.OUTPUT_DIR, EXTERN_H), 'w') as f:
270      f.write(self.extern_header.format(
271        filename=EXTERN_H, externs='\n'.join(externs)))
272
273    chunks.append('\tNULL,') # terminate list
274    chunks.append('};')
275    return '\n'.join(chunks)
276
277  def save(self):
278    filename=os.path.join(self.OUTPUT_DIR, self.SET_C)
279
280    try:
281      os.makedirs(os.path.join(self.OUTPUT_DIR, self.ITEMS_DIR))
282    except:
283      pass
284    with open(filename,'w') as f:
285      f.write( self.to_listinit() )
286      f.write( '\n' )
287
288
289class RouteValues(CSVCollection):
290  MKFILE_SEGMENTS = 'route-values.mk'
291  SET_C = 'ni_route_values.c'
292  ITEMS_DIR = 'ni_route_values'
293  EXTERN_H = 'all.h'
294  OUTPUT_DIR = 'c'
295
296  output_file_top = """\
297// SPDX-License-Identifier: GPL-2.0+
298/*
299 *  comedi/drivers/ni_routing/{filename}
300 *  Route information for NI boards.
301 *
302 *  COMEDI - Linux Control and Measurement Device Interface
303 *  Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu>
304 *
305 *  This program is free software; you can redistribute it and/or modify
306 *  it under the terms of the GNU General Public License as published by
307 *  the Free Software Foundation; either version 2 of the License, or
308 *  (at your option) any later version.
309 *
310 *  This program is distributed in the hope that it will be useful,
311 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
312 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
313 *  GNU General Public License for more details.
314 */
315
316/*
317 * This file includes the tables that are a list of all the values of various
318 * signals routes available on NI hardware.  In many cases, one does not
319 * explicitly make these routes, rather one might indicate that something is
320 * used as the source of one particular trigger or another (using
321 * *_src=TRIG_EXT).
322 *
323 * The contents of this file are generated using the tools in
324 * comedi/drivers/ni_routing/tools
325 *
326 * Please use those tools to help maintain the contents of this file.
327 */
328
329#include "ni_route_values.h"
330#include "{extern_h}"\
331""".format(filename=SET_C, extern_h=os.path.join(ITEMS_DIR, EXTERN_H))
332
333  extern_header = """\
334/* SPDX-License-Identifier: GPL-2.0+ */
335/*
336 *  comedi/drivers/ni_routing/{filename}
337 *  List of valid routes for specific NI boards.
338 *
339 *  COMEDI - Linux Control and Measurement Device Interface
340 *  Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu>
341 *
342 *  This program is free software; you can redistribute it and/or modify
343 *  it under the terms of the GNU General Public License as published by
344 *  the Free Software Foundation; either version 2 of the License, or
345 *  (at your option) any later version.
346 *
347 *  This program is distributed in the hope that it will be useful,
348 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
349 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
350 *  GNU General Public License for more details.
351 */
352
353/*
354 * The contents of this file are generated using the tools in
355 * comedi/drivers/ni_routing/tools
356 *
357 * Please use those tools to help maintain the contents of this file.
358 */
359
360#ifndef _COMEDI_DRIVERS_NI_ROUTING_NI_ROUTE_VALUES_EXTERN_H
361#define _COMEDI_DRIVERS_NI_ROUTING_NI_ROUTE_VALUES_EXTERN_H
362
363#include "../ni_route_values.h"
364
365{externs}
366
367#endif //_COMEDI_DRIVERS_NI_ROUTING_NI_ROUTE_VALUES_EXTERN_H
368"""
369
370  single_output_file_top = """\
371// SPDX-License-Identifier: GPL-2.0+
372/*
373 *  comedi/drivers/ni_routing/{filename}
374 *  Route information for {sheet} boards.
375 *
376 *  COMEDI - Linux Control and Measurement Device Interface
377 *  Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu>
378 *
379 *  This program is free software; you can redistribute it and/or modify
380 *  it under the terms of the GNU General Public License as published by
381 *  the Free Software Foundation; either version 2 of the License, or
382 *  (at your option) any later version.
383 *
384 *  This program is distributed in the hope that it will be useful,
385 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
386 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
387 *  GNU General Public License for more details.
388 */
389
390/*
391 * This file includes a list of all the values of various signals routes
392 * available on NI 660x hardware.  In many cases, one does not explicitly make
393 * these routes, rather one might indicate that something is used as the source
394 * of one particular trigger or another (using *_src=TRIG_EXT).
395 *
396 * The contents of this file can be generated using the tools in
397 * comedi/drivers/ni_routing/tools.  This file also contains specific notes to
398 * this family of devices.
399 *
400 * Please use those tools to help maintain the contents of this file, but be
401 * mindful to not lose the notes already made in this file, since these notes
402 * are critical to a complete undertsanding of the register values of this
403 * family.
404 */
405
406#include "../ni_route_values.h"
407#include "{extern_h}"
408
409const struct family_route_values {table_name} = {{\
410"""
411
412  def __init__(self, pattern='csv/route_values/*.csv'):
413    super(RouteValues,self).__init__(pattern)
414
415  def to_structinit(self):
416    chunks = [ self.output_file_top,
417      '',
418      'const struct family_route_values *const ni_all_route_values[] = {'
419    ]
420    # put the sheets in lexical order for consistency
421    sheets = sorted(self.items(), key=lambda i : i[0] )
422
423    externs = []
424    objs = [c_to_o(self.SET_C)]
425
426    for sheet,D in sheets:
427      S = sheet.lower()
428      fam_table_name = '{}_route_values'.format(S.replace('-','_'))
429      sheet_filename = os.path.join(self.ITEMS_DIR,'{}.c'.format(S))
430      externs.append('extern const struct family_route_values {};'.format(fam_table_name))
431
432      chunks.append('\t&{},'.format(fam_table_name))
433
434      s_chunks = [
435        self.single_output_file_top.format(
436          filename    = sheet_filename,
437          sheet       = sheet.upper(),
438          table_name  = fam_table_name,
439          extern_h    = self.EXTERN_H,
440        ),
441        routedict_to_structinit_single(S, D),
442        '};',
443      ]
444
445      objs.append(c_to_o(sheet_filename))
446
447      with open(os.path.join(self.OUTPUT_DIR, sheet_filename), 'w') as f:
448        f.write('\n'.join(s_chunks))
449        f.write( '\n' )
450
451    with open(os.path.join(self.OUTPUT_DIR, self.MKFILE_SEGMENTS), 'w') as f:
452      f.write('# This is the segment that should be included in comedi/drivers/Makefile\n')
453      f.write('ni_routing-objs\t\t\t\t+= \\\n')
454      f.write('\n'.join(objs))
455      f.write('\n')
456
457    EXTERN_H = os.path.join(self.ITEMS_DIR, self.EXTERN_H)
458    with open(os.path.join(self.OUTPUT_DIR, EXTERN_H), 'w') as f:
459      f.write(self.extern_header.format(
460        filename=EXTERN_H, externs='\n'.join(externs)))
461
462    chunks.append('\tNULL,') # terminate list
463    chunks.append('};')
464    return '\n'.join(chunks)
465
466  def save(self):
467    filename=os.path.join(self.OUTPUT_DIR, self.SET_C)
468
469    try:
470      os.makedirs(os.path.join(self.OUTPUT_DIR, self.ITEMS_DIR))
471    except:
472      pass
473    with open(filename,'w') as f:
474      f.write( self.to_structinit() )
475      f.write( '\n' )
476
477
478
479if __name__ == '__main__':
480  import argparse
481  parser = argparse.ArgumentParser()
482  parser.add_argument( '--route_values', action='store_true',
483    help='Extract route values from csv/route_values/*.csv' )
484  parser.add_argument( '--device_routes', action='store_true',
485    help='Extract route values from csv/device_routes/*.csv' )
486  args = parser.parse_args()
487  KL = list()
488  if args.route_values:
489    KL.append( RouteValues )
490  if args.device_routes:
491    KL.append( DeviceRoutes )
492  if not KL:
493    parser.error('nothing to do...')
494  for K in KL:
495    doc = K()
496    doc.save()
497