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