Use env to find python
[bitfield] / bitfield
1 #!/usr/bin/env python
2 #
3 # Utility to decode register values
4 # Copyright (c) 2006 Jeremy Kerr <jk@ozlabs.org>
5 # Released under the GNU General Public License version 2 or later
6 #
7 # Documentation and updates at: http://ozlabs.org/~jk/code/bitfield
8
9 import os
10 import sys
11 import pprint
12 from pyparsing import Literal, Word, ZeroOrMore, Group, Dict, Optional, \
13         printables, ParseException, restOfLine
14 from getopt import getopt, GetoptError
15
16 # List of paths to look for configuration files. If a directory is specified,
17 # it will be (recursively) scanned for .conf files.
18 configs = ["/etc/bitfield.d", "/etc/bitfield",
19                 os.path.join(os.getenv("HOME"), ".bitfield.d"),
20                 os.path.join(os.getenv("HOME"), ".bitfield.conf")]
21
22 class bitfield:
23         def __init__(self, bits, name):
24                 self.bits = bits
25                 self.name = name
26                 self.values = {}
27
28         def width(self):
29                 return len(self.bits)
30
31         def add_value(self, value, description):
32                 self.values[int(value)] = description
33
34         def mask(self, reg_width, value):
35                 ret = 0
36                 out_len = len(self.bits)
37                 for out_bit in range(0, out_len):
38                         in_bit = self.bits[out_bit]
39                         # shift this bit down to the LSB (and mask the rest)
40                         i = (value >> (reg_width - in_bit - 1)) & 1
41                         # shift back to the output position in the field
42                         i <<= out_len - out_bit - 1
43                         ret |= i
44                 return ret
45
46         def value(self, value):
47                 if value in self.values:
48                         return self.values[value]
49                 return None
50
51         @staticmethod
52         def mask_and_shift_to_bits(width, mask, shift):
53                 bits = []
54                 val = mask << shift
55                 for i in range(0, width):
56                         if mask & (1 << i):
57                                 bits.append(width - i - 1 - shift)
58                 return bits
59
60         @staticmethod
61         def parse_bitfield(line, reg):
62                 a = line.split(None, 1)
63                 if len(a) != 2:
64                         return None
65                 (range_str, name) = a
66
67                 bits = []
68                 for s in range_str.split(','):
69                         if ':' in s:
70                                 (start, end) = map( \
71                                         lambda s: reg.bit_number(int(s)),
72                                         s.split(':'))
73                                 start = reg.bit_number(int(start))
74                                 end = reg.bit_number(int(end))
75                                 bits.extend(range(start, end + 1, 1))
76                         elif '<<' in s:
77                                 (mask, shift) = map(lambda s: int(s.strip()),
78                                         s.split('<<'))
79                                 bits.extend(bitfield.mask_and_shift_to_bits( \
80                                         reg.width, mask, shift))
81                         else:
82                                 bits.append(reg.bit_number(int(s)))
83
84                 return bitfield(bits, name)
85
86
87
88         @staticmethod
89         def parse_value(line):
90                 a = line.split(None, 1)
91                 if len(a) != 2:
92                         return None
93                 return a
94
95 class register:
96         bit_0_is_msb = 0
97         bit_0_is_lsb = 1
98
99         def __init__(self, id):
100                 self.id = id
101                 self.fields = []
102                 # set defaults
103                 self.name = None
104                 self.bit_order = self.bit_0_is_msb
105                 self.width = 64
106
107         def add_field(self, field):
108                 self.fields.append(field)
109
110         def decode(self, value, ignore_zero):
111                 field_width = (self.width + 3) / 4
112                 name_width = max(map(lambda f: len(f.name), self.fields))
113
114                 str = "0x%0*lx [%d]\n" % (field_width, value, value)
115
116                 for field in self.fields:
117                         v = field.mask(self.width, value);
118                         if ignore_zero and v == 0:
119                                 continue
120                         desc = field.value(v)
121                         if desc is not None:
122                                 str += "%*s: 0x%x [%s]\n" \
123                                         % (name_width, field.name, v, desc)
124                         else:
125                                 str += "%*s: 0x%x\n" \
126                                         % (name_width, field.name, v)
127                 return str
128
129         def bit_number(self, number):
130                 if self.bit_order == self.bit_0_is_lsb:
131                         number = self.width - 1 - number
132                 return number
133
134 def list_regs(regs):
135         for (id, r) in regs.iteritems():
136                 print "%18s : %s" % (id, r.name)
137
138 def search_regs(regs, str):
139         return dict((k, regs[k]) for k in regs \
140                         if str.lower() in regs[k].name.lower() + k.lower())
141
142 class ConfigurationError(Exception):
143         def __init__(self, file, message):
144                 self.file = file
145                 self.message = message
146
147 def parse_config(bnf, regs, file):
148         f = open(file)
149
150         tokens = bnf.parseString(f.read())
151
152         order_map = {'bit-0-is-lsb':    register.bit_0_is_lsb,
153                         'bit-0-is-msb': register.bit_0_is_msb,
154                         'ibm':          register.bit_0_is_msb,
155                         'default':      register.bit_0_is_msb}
156
157         for tok in tokens:
158                 ts = tok.asList()
159                 id = ts.pop(0)
160
161                 if regs.has_key(id):
162                         raise ConfigurationError(file,
163                                 "Register %s is already defined" % id)
164
165                 reg = register(id)
166
167                 alias_id = None
168                 fields = []
169
170                 for t in ts:
171                         if t[0] == 'name':
172                                 name = t[1]
173                                 reg.name = name.strip()
174                         elif t[0] == 'width':
175                                 reg.width = int(t[1])
176                         elif t[0] == 'field':
177                                 f = bitfield.parse_bitfield(t[1], reg)
178                                 if f is None:
179                                         raise ConfigurationError(file,
180                                                 "Invalid field in %s" % id)
181                                 fields.append(f)
182                         elif t[0] == 'value':
183                                 if len(fields) == 0:
184                                         raise ConfigurationError(file,
185                                                 "No field for value in %s" % id)
186                                 v = bitfield.parse_value(t[1])
187                                 if v is None:
188                                         raise ConfigurationError(file,
189                                                 "Invalid value in %s" % id)
190
191                                 fields[-1].add_value(v[0], v[1])
192                         elif t[0] == 'order':
193                                 if len(fields) != 0:
194                                         raise ConfigurationError(file,
195                                                 ("bit order defined after " \
196                                                 + "fields in %s") % id)
197
198                                 order_str = t[1].strip().lower()
199                                 order_str = order_str.replace(' ', '-')
200
201                                 if order_str not in order_map.keys():
202                                         raise ConfigurationError(file,
203                                                 "Invalid bit order %s in %s" % \
204                                                 (order_str, id))
205                                 reg.bit_order = order_map[order_str]
206
207                         elif t[0] == 'alias':
208                                 alias_id = t[1].strip()
209
210                 if alias_id is not None:
211                         if reg.name is not None or fields != []:
212                                 raise ConfigurationError(file, ("Definiton " \
213                                         + "for %s is an alias, but has other " \
214                                         + "attributes") % id)
215
216                         if not regs.has_key(alias_id):
217                                 raise ConfigurationError(file, "Aliasing "
218                                         "non-existent register %s (from %s)" \
219                                         % (alias_id, id))
220
221                         reg = regs[alias_id]
222                         continue
223
224                 if reg.name is None or reg.name == '':
225                         raise ConfigurationError(file,
226                                 "No name for entry %s" % id)
227
228                 if len(fields) == 0:
229                         raise ConfigurationError(file,
230                                 "Register %s has no fields" % id)
231
232                 for f in fields:
233                         reg.add_field(f)
234
235                 regs[id] = reg
236
237 def parse_config_dir(data, dir, fnames):
238         (bnf, regs) = data
239         for fname in fnames:
240                 full_fname = os.path.join(dir, fname)
241
242                 if os.path.isdir(full_fname):
243                         continue
244
245                 if fname.endswith('.conf'):
246                         parse_config(bnf, regs, full_fname)
247
248 def parse_all_configs(configs):
249         regs = {}
250
251         # set up the bnf to be used for each file
252         lbrack = Literal("[").suppress()
253         rbrack = Literal("]").suppress()
254         colon  = Literal(":").suppress()
255         semi   = Literal(";")
256
257         comment = semi + Optional(restOfLine)
258
259         nonrbrack = "".join([c for c in printables if c != "]"]) + " \t"
260         noncolon  = "".join([c for c in printables if c != ":"]) + " \t"
261
262         sectionDef = lbrack + Word(nonrbrack) + rbrack
263         keyDef = ~lbrack + Word(noncolon) + colon + restOfLine
264
265         bnf = Dict(ZeroOrMore(Group(sectionDef + ZeroOrMore(Group(keyDef)))))
266         bnf.ignore(comment)
267
268         # bundle into a single var that can be passed to os.path.walk
269         conf_data = (bnf, regs)
270
271         for conf in configs:
272                 if not os.path.exists(conf):
273                         continue
274                 if os.path.isdir(conf):
275                         os.path.walk(conf, parse_config_dir, conf_data)
276                 else:
277                         parse_config(bnf, regs, conf)
278         return regs
279
280 def usage(prog):
281         print "Usage: %s <-l> | <-s pattern> | [-n] register [value...]" % prog
282
283 def decode_value(reg, value, options):
284         try:
285                 i = long(value, 0)
286         except ValueError, e:
287                 print "error: invalid value '%s'" % value
288                 return
289
290         if i > ((1 << reg.width) - 1):
291                 print ("error: value '%s' is too large " + \
292                         "for %d-bit register '%s'") % (value, reg.width, reg.id)
293                 return
294
295         print reg.decode(i, options['non-zero'])
296
297
298 def main():
299         try:
300                 (opts, args) = getopt(sys.argv[1:], "hlns:", \
301                         ["help", "list", "non-zero", "search="])
302         except GetoptError:
303                 usage(sys.argv[0])
304                 return 1
305
306         try:
307                 regs = parse_all_configs(configs)
308         except ConfigurationError, e:
309                 print "Error parsing configuration file %s:\n\t%s" % \
310                         (e.file, e.message)
311                 return 1
312
313         if regs == {}:
314                 print "No configuration available"
315                 return 1
316
317         options = {}
318         options['non-zero'] = False
319
320         for o, a in opts:
321                 if o in ("-h", "--help"):
322                         usage(sys.argv[0])
323                         return
324
325                 if o in ("-l", "--list"):
326                         list_regs(regs)
327                         return
328
329                 if o in ("-s", "--search"):
330                         list_regs(search_regs(regs, a))
331                         return
332
333                 if o in ("-n", "--non-zero"):
334                         options['non-zero'] = True
335
336         if not args:
337                 usage(sys.argv[0])
338                 return 1
339
340         reg_id = args.pop(0)
341         if not regs.has_key(reg_id):
342                 print "No such register '%s'" % reg_id
343                 return 1
344
345         reg = regs[reg_id]
346         print "decoding as %s" % reg.name
347
348         if args:
349                 value_iter = args.__iter__()
350         else:
351                 value_iter = iter(sys.stdin.readline, '')
352
353         try:
354                 for value in value_iter:
355                         decode_value(reg, value.strip(), options)
356         except KeyboardInterrupt, e:
357                 pass
358
359         return 0
360
361 if __name__ == "__main__":
362         sys.exit(main())