Skip to content

Commit 0581611

Browse files
authored
Initial add of EEPROM, LED, PSU and SFP package files, as well as setup.py file (sonic-net#1)
1 parent e6727bc commit 0581611

15 files changed

Lines changed: 4464 additions & 0 deletions

setup.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from setuptools import setup
2+
3+
setup(
4+
name='sonic-platform-common',
5+
version='1.0',
6+
description='Platform-specific peripheral hardware interface APIs for SONiC',
7+
license='Apache 2.0',
8+
author='SONiC Team',
9+
author_email='linuxnetdev@microsoft.com',
10+
url='https://github.com/Azure/sonic-platform-common',
11+
maintainer='Joe LeVeque',
12+
maintainer_email='jolevequ@microsoft.com',
13+
packages=[
14+
'sonic_eeprom',
15+
'sonic_led',
16+
'sonic_psu',
17+
'sonic_sfp',
18+
],
19+
classifiers=[
20+
'Development Status :: 3 - Alpha',
21+
'Environment :: Plugins',
22+
'Intended Audience :: Developers',
23+
'Intended Audience :: Information Technology',
24+
'Intended Audience :: System Administrators',
25+
'License :: OSI Approved :: Apache Software License',
26+
'Natural Language :: English',
27+
'Operating System :: POSIX :: Linux',
28+
'Programming Language :: Python :: 2.7',
29+
'Programming Language :: Python :: 3.6',
30+
'Topic :: Utilities',
31+
],
32+
keywords='sonic SONiC platform hardware interface api API',
33+
)

sonic_eeprom/__init__.py

Whitespace-only changes.

sonic_eeprom/eeprom_base.py

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
#! /usr/bin/python
2+
# Copyright 2012 Cumulus Networks LLC, all rights reserved
3+
4+
#############################################################################
5+
# Base eeprom class containing the main logic for reading, writing, and
6+
# setting the eeprom. The format definition is a list of tuples of:
7+
# ('data name', 'data type', 'size in bytes')
8+
# data type is one of 's', 'C', and 'x' (string, char, and ignore)
9+
# 'burn' as a data name indicates the corresponding number of bytes are to
10+
# be ignored
11+
12+
try:
13+
import exceptions
14+
import binascii
15+
import optparse
16+
import os
17+
import io
18+
import sys
19+
import struct
20+
import subprocess
21+
import fcntl
22+
except ImportError, e:
23+
raise ImportError (str(e) + "- required module not found")
24+
25+
26+
class EepromDecoder(object):
27+
def __init__(self, path, format, start, status, readonly):
28+
self.p = path
29+
self.f = format
30+
self.s = start
31+
self.u = status
32+
self.r = readonly
33+
self.cache_name = None
34+
self.cache_update_needed = False
35+
self.lock_file = None
36+
37+
def check_status(self):
38+
if self.u <> '':
39+
F = open(self.u, "r")
40+
d = F.readline().rstrip()
41+
F.close()
42+
return d
43+
else:
44+
return 'ok'
45+
46+
def set_cache_name(self, name):
47+
# before accessing the eeprom we acquire an exclusive lock on the eeprom file.
48+
# this will prevent a race condition where multiple instances of this app
49+
# could try to update the cache at the same time
50+
self.cache_name = name
51+
self.lock_file = open(self.p, 'r')
52+
fcntl.flock(self.lock_file, fcntl.LOCK_EX)
53+
54+
def is_read_only(self):
55+
return self.r
56+
57+
def decoder(self, s, t):
58+
return t
59+
60+
def encoder(self, I, v):
61+
return v
62+
63+
def checksum_field_size(self):
64+
return 4 # default
65+
66+
def is_checksum_field(self, I):
67+
return I[0] == 'crc' # default
68+
69+
def checksum_type(self):
70+
return 'crc32'
71+
72+
def encode_checksum(self, crc):
73+
if self.checksum_field_size() == 4:
74+
return struct.pack('>I', crc)
75+
elif self.checksum_field_size() == 1:
76+
return struct.pack('>B', crc)
77+
print 'checksum type not yet supported'
78+
exit(1)
79+
80+
def compute_2s_complement(self, e, size):
81+
crc = 0
82+
loc = 0
83+
end = len(e)
84+
while loc <> end:
85+
crc += int('0x' + binascii.b2a_hex(e[loc:loc+size]), 0)
86+
loc += size
87+
T = 1 << (size * 8)
88+
return (T - crc) & (T - 1)
89+
90+
def compute_dell_crc(self, message):
91+
poly = 0x8005
92+
reg = 0x0000
93+
message += '\x00\x00'
94+
for byte in message:
95+
mask = 0x80
96+
while (mask > 0):
97+
reg<<=1
98+
if ord(byte) & mask:
99+
reg += 1
100+
mask>>=1
101+
if reg > 0xffff:
102+
reg &= 0xffff
103+
reg ^= poly
104+
return reg
105+
106+
def calculate_checksum(self, e):
107+
if self.checksum_type() == 'crc32':
108+
return binascii.crc32(e) & 0xffffffff
109+
110+
if self.checksum_type() == '2s-complement':
111+
size = self.checksum_field_size()
112+
return self.compute_2s_complement(e, size)
113+
114+
if self.checksum_type() == 'dell-crc':
115+
return self.compute_dell_crc(e)
116+
print 'checksum type not yet supported'
117+
exit(1)
118+
119+
def is_checksum_valid(self, e):
120+
offset = 0 - self.checksum_field_size()
121+
crc = self.calculate_checksum(e[:offset])
122+
123+
loc = 0
124+
for I in self.f:
125+
end = loc + I[2]
126+
t = e[loc:end]
127+
loc = end
128+
if self.is_checksum_field(I):
129+
i = self.decoder(I[0], t)
130+
if int(i, 0) == crc:
131+
return (True, crc)
132+
else:
133+
return (False, crc)
134+
else:
135+
continue
136+
return (False, crc)
137+
138+
def decode_eeprom(self, e):
139+
loc = 0
140+
for I in self.f:
141+
end = loc + I[2]
142+
t = e[loc:end]
143+
loc = end
144+
if I[0] == 'burn':
145+
continue
146+
elif I[1] == 's':
147+
i = t
148+
else:
149+
i = self.decoder(I[0], t)
150+
print "%-20s: %s" %(I[0], i)
151+
152+
def set_eeprom(self, e, cmd_args):
153+
line = ''
154+
loc = 0
155+
ndict = {}
156+
fields = list(I[0] for I in list(self.f))
157+
if len(cmd_args):
158+
for arg in cmd_args[0].split(','):
159+
k, v = arg.split('=')
160+
k = k.strip()
161+
v = v.strip()
162+
if k not in fields:
163+
print "Error: invalid field '%s'" %(k)
164+
exit(1)
165+
ndict[k] = v
166+
167+
for I in self.f:
168+
# print the original value
169+
end = loc + I[2]
170+
sl = e[loc:end]
171+
loc = end
172+
if I[0] == 'burn':
173+
#line += sl
174+
# fill with zeros
175+
line = line.ljust(len(line) + I[2], '\x00')
176+
continue
177+
elif I[1] == 's':
178+
i = sl
179+
else:
180+
i = self.decoder(I[0], sl)
181+
182+
if len(cmd_args) == 0:
183+
if self.is_checksum_field(I):
184+
print ("%-20s: %s " %(I[0], i))
185+
continue
186+
187+
# prompt for new value
188+
v = raw_input("%-20s: [%s] " %(I[0], i))
189+
if v == '':
190+
v = i
191+
else:
192+
if I[0] not in ndict.keys():
193+
v = i
194+
else:
195+
v = ndict[I[0]]
196+
197+
line += self.encoder(I, v)
198+
199+
# compute and append crc at the end
200+
crc = self.encode_checksum(self.calculate_checksum(line))
201+
202+
line += crc
203+
204+
return line
205+
206+
def open_eeprom(self):
207+
'''
208+
Open the EEPROM device file.
209+
If a cache file exists, use that instead of the EEPROM.
210+
'''
211+
using_eeprom = True
212+
eeprom_file = self.p
213+
try:
214+
if os.path.isfile(self.cache_name):
215+
eeprom_file = self.cache_name
216+
using_eeprom = False
217+
except:
218+
pass
219+
self.cache_update_needed = using_eeprom
220+
return io.open(eeprom_file, "rb")
221+
222+
def read_eeprom(self):
223+
sizeof_info = 0
224+
for I in self.f:
225+
sizeof_info += I[2]
226+
o = self.read_eeprom_bytes(sizeof_info)
227+
return o
228+
229+
def read_eeprom_bytes(self, byteCount, offset=0):
230+
F = self.open_eeprom()
231+
F.seek(self.s + offset)
232+
o = F.read(byteCount)
233+
if len(o) != byteCount:
234+
raise RuntimeError("expected to read %d bytes from %s, " \
235+
%(byteCount, self.p) +
236+
"but only read %d" %(len(o)))
237+
F.close()
238+
return o
239+
240+
def write_eeprom(self, e):
241+
F = open(self.p, "wb")
242+
F.seek(self.s)
243+
F.write(e)
244+
F.close()
245+
self.write_cache(e)
246+
247+
def write_cache(self, e):
248+
if self.cache_name:
249+
F = open(self.cache_name, "wb")
250+
F.seek(self.s)
251+
F.write(e)
252+
F.close()
253+
254+
def update_cache(self, e):
255+
if self.cache_update_needed:
256+
self.write_cache(e)
257+
fcntl.flock(self.lock_file, fcntl.LOCK_UN)
258+
259+
def diff_mac(self, mac1, mac2):
260+
if mac1 == '' or mac2 == '':
261+
return 0
262+
mac1_octets = []
263+
mac1_octets = mac1.split(':')
264+
mac1val = int(mac1_octets[5], 16) | int(mac1_octets[4], 16) << 8 | int(mac1_octets[3], 16) << 16
265+
mac2_octets = []
266+
mac2_octets = mac2.split(':')
267+
mac2val = int(mac2_octets[5], 16) | int(mac2_octets[4], 16) << 8 | int(mac2_octets[3], 16) << 16
268+
# check oui matches
269+
if (mac1_octets[0] != mac2_octets[0]
270+
or mac1_octets[1] != mac2_octets[1]
271+
or mac1_octets[2] != mac2_octets[2]) :
272+
return 0
273+
274+
if mac2val < mac1val:
275+
return 0
276+
277+
return (mac2val - mac1val)
278+
279+
def increment_mac(self, mac):
280+
if mac != "":
281+
mac_octets = []
282+
mac_octets = mac.split(':')
283+
ret_mac = int(mac_octets[5], 16) | int(mac_octets[4], 16) << 8 | int(mac_octets[3], 16) << 16
284+
ret_mac = ret_mac + 1
285+
286+
if (ret_mac & 0xff000000):
287+
print 'Error: increment carries into OUI'
288+
return ''
289+
290+
mac_octets[5] = hex(ret_mac & 0xff)[2:].zfill(2)
291+
mac_octets[4] = hex((ret_mac >> 8) & 0xff)[2:].zfill(2)
292+
mac_octets[3] = hex((ret_mac >> 16) & 0xff)[2:].zfill(2)
293+
294+
return ':'.join(mac_octets).upper()
295+
296+
return ''
297+
298+
@classmethod
299+
def find_field(cls, e, name):
300+
if not hasattr(cls, 'brd_fmt'):
301+
raise RuntimeError("Class %s does not have brb_fmt" % cls)
302+
if not e:
303+
raise RuntimeError("EEPROM can not be empty")
304+
brd_fmt = cls.brd_fmt
305+
loc = 0
306+
for f in brd_fmt:
307+
end = loc + f[2]
308+
t = e[loc:end]
309+
loc = end
310+
if f[0] == name:
311+
return t
312+
313+
def base_mac_addr(self, e):
314+
'''
315+
Returns the base MAC address found in the EEPROM.
316+
317+
Sub-classes must override this method as reading the EEPROM
318+
and finding the base MAC address entails platform specific
319+
details.
320+
321+
See also mgmtaddrstr() and switchaddrstr().
322+
'''
323+
print "ERROR: Platform did not implement base_mac_addr()"
324+
raise NotImplementedError
325+
326+
def mgmtaddrstr(self, e):
327+
'''
328+
Returns the base MAC address to use for the Ethernet
329+
management interface(s) on the CPU complex.
330+
331+
By default this is the same as the base MAC address listed in
332+
the EEPROM.
333+
334+
See also switchaddrstr().
335+
'''
336+
return self.base_mac_addr(e)
337+
338+
def switchaddrstr(self, e):
339+
'''
340+
Returns the base MAC address to use for the switch ASIC
341+
interfaces.
342+
343+
By default this is *next* address after the base MAC address
344+
listed in the EEPROM.
345+
346+
See also mgmtaddrstr().
347+
'''
348+
return self.increment_mac(self.base_mac_addr(e))
349+
350+
def switchaddrrange(self, e):
351+
# this function is in the base class only to catch errors
352+
# the platform specific import should have an override of this method
353+
# to provide the allocated mac range from syseeprom or flash sector or
354+
# wherever that platform stores this info
355+
print "Platform did not indicate allocated mac address range"
356+
raise NotImplementedError
357+
358+
def serial_number_str(self, e):
359+
raise NotImplementedError("Platform did not indicate serial number")

0 commit comments

Comments
 (0)