forked from sonic-net/sonic-platform-daemons
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial add of EEPROM, LED, PSU and SFP package files, as well as set…
…up.py file (sonic-net#1)
- Loading branch information
Showing
15 changed files
with
4,464 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from setuptools import setup | ||
|
||
setup( | ||
name='sonic-platform-common', | ||
version='1.0', | ||
description='Platform-specific peripheral hardware interface APIs for SONiC', | ||
license='Apache 2.0', | ||
author='SONiC Team', | ||
author_email='[email protected]', | ||
url='https://github.com/Azure/sonic-platform-common', | ||
maintainer='Joe LeVeque', | ||
maintainer_email='[email protected]', | ||
packages=[ | ||
'sonic_eeprom', | ||
'sonic_led', | ||
'sonic_psu', | ||
'sonic_sfp', | ||
], | ||
classifiers=[ | ||
'Development Status :: 3 - Alpha', | ||
'Environment :: Plugins', | ||
'Intended Audience :: Developers', | ||
'Intended Audience :: Information Technology', | ||
'Intended Audience :: System Administrators', | ||
'License :: OSI Approved :: Apache Software License', | ||
'Natural Language :: English', | ||
'Operating System :: POSIX :: Linux', | ||
'Programming Language :: Python :: 2.7', | ||
'Programming Language :: Python :: 3.6', | ||
'Topic :: Utilities', | ||
], | ||
keywords='sonic SONiC platform hardware interface api API', | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,359 @@ | ||
#! /usr/bin/python | ||
# Copyright 2012 Cumulus Networks LLC, all rights reserved | ||
|
||
############################################################################# | ||
# Base eeprom class containing the main logic for reading, writing, and | ||
# setting the eeprom. The format definition is a list of tuples of: | ||
# ('data name', 'data type', 'size in bytes') | ||
# data type is one of 's', 'C', and 'x' (string, char, and ignore) | ||
# 'burn' as a data name indicates the corresponding number of bytes are to | ||
# be ignored | ||
|
||
try: | ||
import exceptions | ||
import binascii | ||
import optparse | ||
import os | ||
import io | ||
import sys | ||
import struct | ||
import subprocess | ||
import fcntl | ||
except ImportError, e: | ||
raise ImportError (str(e) + "- required module not found") | ||
|
||
|
||
class EepromDecoder(object): | ||
def __init__(self, path, format, start, status, readonly): | ||
self.p = path | ||
self.f = format | ||
self.s = start | ||
self.u = status | ||
self.r = readonly | ||
self.cache_name = None | ||
self.cache_update_needed = False | ||
self.lock_file = None | ||
|
||
def check_status(self): | ||
if self.u <> '': | ||
F = open(self.u, "r") | ||
d = F.readline().rstrip() | ||
F.close() | ||
return d | ||
else: | ||
return 'ok' | ||
|
||
def set_cache_name(self, name): | ||
# before accessing the eeprom we acquire an exclusive lock on the eeprom file. | ||
# this will prevent a race condition where multiple instances of this app | ||
# could try to update the cache at the same time | ||
self.cache_name = name | ||
self.lock_file = open(self.p, 'r') | ||
fcntl.flock(self.lock_file, fcntl.LOCK_EX) | ||
|
||
def is_read_only(self): | ||
return self.r | ||
|
||
def decoder(self, s, t): | ||
return t | ||
|
||
def encoder(self, I, v): | ||
return v | ||
|
||
def checksum_field_size(self): | ||
return 4 # default | ||
|
||
def is_checksum_field(self, I): | ||
return I[0] == 'crc' # default | ||
|
||
def checksum_type(self): | ||
return 'crc32' | ||
|
||
def encode_checksum(self, crc): | ||
if self.checksum_field_size() == 4: | ||
return struct.pack('>I', crc) | ||
elif self.checksum_field_size() == 1: | ||
return struct.pack('>B', crc) | ||
print 'checksum type not yet supported' | ||
exit(1) | ||
|
||
def compute_2s_complement(self, e, size): | ||
crc = 0 | ||
loc = 0 | ||
end = len(e) | ||
while loc <> end: | ||
crc += int('0x' + binascii.b2a_hex(e[loc:loc+size]), 0) | ||
loc += size | ||
T = 1 << (size * 8) | ||
return (T - crc) & (T - 1) | ||
|
||
def compute_dell_crc(self, message): | ||
poly = 0x8005 | ||
reg = 0x0000 | ||
message += '\x00\x00' | ||
for byte in message: | ||
mask = 0x80 | ||
while (mask > 0): | ||
reg<<=1 | ||
if ord(byte) & mask: | ||
reg += 1 | ||
mask>>=1 | ||
if reg > 0xffff: | ||
reg &= 0xffff | ||
reg ^= poly | ||
return reg | ||
|
||
def calculate_checksum(self, e): | ||
if self.checksum_type() == 'crc32': | ||
return binascii.crc32(e) & 0xffffffff | ||
|
||
if self.checksum_type() == '2s-complement': | ||
size = self.checksum_field_size() | ||
return self.compute_2s_complement(e, size) | ||
|
||
if self.checksum_type() == 'dell-crc': | ||
return self.compute_dell_crc(e) | ||
print 'checksum type not yet supported' | ||
exit(1) | ||
|
||
def is_checksum_valid(self, e): | ||
offset = 0 - self.checksum_field_size() | ||
crc = self.calculate_checksum(e[:offset]) | ||
|
||
loc = 0 | ||
for I in self.f: | ||
end = loc + I[2] | ||
t = e[loc:end] | ||
loc = end | ||
if self.is_checksum_field(I): | ||
i = self.decoder(I[0], t) | ||
if int(i, 0) == crc: | ||
return (True, crc) | ||
else: | ||
return (False, crc) | ||
else: | ||
continue | ||
return (False, crc) | ||
|
||
def decode_eeprom(self, e): | ||
loc = 0 | ||
for I in self.f: | ||
end = loc + I[2] | ||
t = e[loc:end] | ||
loc = end | ||
if I[0] == 'burn': | ||
continue | ||
elif I[1] == 's': | ||
i = t | ||
else: | ||
i = self.decoder(I[0], t) | ||
print "%-20s: %s" %(I[0], i) | ||
|
||
def set_eeprom(self, e, cmd_args): | ||
line = '' | ||
loc = 0 | ||
ndict = {} | ||
fields = list(I[0] for I in list(self.f)) | ||
if len(cmd_args): | ||
for arg in cmd_args[0].split(','): | ||
k, v = arg.split('=') | ||
k = k.strip() | ||
v = v.strip() | ||
if k not in fields: | ||
print "Error: invalid field '%s'" %(k) | ||
exit(1) | ||
ndict[k] = v | ||
|
||
for I in self.f: | ||
# print the original value | ||
end = loc + I[2] | ||
sl = e[loc:end] | ||
loc = end | ||
if I[0] == 'burn': | ||
#line += sl | ||
# fill with zeros | ||
line = line.ljust(len(line) + I[2], '\x00') | ||
continue | ||
elif I[1] == 's': | ||
i = sl | ||
else: | ||
i = self.decoder(I[0], sl) | ||
|
||
if len(cmd_args) == 0: | ||
if self.is_checksum_field(I): | ||
print ("%-20s: %s " %(I[0], i)) | ||
continue | ||
|
||
# prompt for new value | ||
v = raw_input("%-20s: [%s] " %(I[0], i)) | ||
if v == '': | ||
v = i | ||
else: | ||
if I[0] not in ndict.keys(): | ||
v = i | ||
else: | ||
v = ndict[I[0]] | ||
|
||
line += self.encoder(I, v) | ||
|
||
# compute and append crc at the end | ||
crc = self.encode_checksum(self.calculate_checksum(line)) | ||
|
||
line += crc | ||
|
||
return line | ||
|
||
def open_eeprom(self): | ||
''' | ||
Open the EEPROM device file. | ||
If a cache file exists, use that instead of the EEPROM. | ||
''' | ||
using_eeprom = True | ||
eeprom_file = self.p | ||
try: | ||
if os.path.isfile(self.cache_name): | ||
eeprom_file = self.cache_name | ||
using_eeprom = False | ||
except: | ||
pass | ||
self.cache_update_needed = using_eeprom | ||
return io.open(eeprom_file, "rb") | ||
|
||
def read_eeprom(self): | ||
sizeof_info = 0 | ||
for I in self.f: | ||
sizeof_info += I[2] | ||
o = self.read_eeprom_bytes(sizeof_info) | ||
return o | ||
|
||
def read_eeprom_bytes(self, byteCount, offset=0): | ||
F = self.open_eeprom() | ||
F.seek(self.s + offset) | ||
o = F.read(byteCount) | ||
if len(o) != byteCount: | ||
raise RuntimeError("expected to read %d bytes from %s, " \ | ||
%(byteCount, self.p) + | ||
"but only read %d" %(len(o))) | ||
F.close() | ||
return o | ||
|
||
def write_eeprom(self, e): | ||
F = open(self.p, "wb") | ||
F.seek(self.s) | ||
F.write(e) | ||
F.close() | ||
self.write_cache(e) | ||
|
||
def write_cache(self, e): | ||
if self.cache_name: | ||
F = open(self.cache_name, "wb") | ||
F.seek(self.s) | ||
F.write(e) | ||
F.close() | ||
|
||
def update_cache(self, e): | ||
if self.cache_update_needed: | ||
self.write_cache(e) | ||
fcntl.flock(self.lock_file, fcntl.LOCK_UN) | ||
|
||
def diff_mac(self, mac1, mac2): | ||
if mac1 == '' or mac2 == '': | ||
return 0 | ||
mac1_octets = [] | ||
mac1_octets = mac1.split(':') | ||
mac1val = int(mac1_octets[5], 16) | int(mac1_octets[4], 16) << 8 | int(mac1_octets[3], 16) << 16 | ||
mac2_octets = [] | ||
mac2_octets = mac2.split(':') | ||
mac2val = int(mac2_octets[5], 16) | int(mac2_octets[4], 16) << 8 | int(mac2_octets[3], 16) << 16 | ||
# check oui matches | ||
if (mac1_octets[0] != mac2_octets[0] | ||
or mac1_octets[1] != mac2_octets[1] | ||
or mac1_octets[2] != mac2_octets[2]) : | ||
return 0 | ||
|
||
if mac2val < mac1val: | ||
return 0 | ||
|
||
return (mac2val - mac1val) | ||
|
||
def increment_mac(self, mac): | ||
if mac != "": | ||
mac_octets = [] | ||
mac_octets = mac.split(':') | ||
ret_mac = int(mac_octets[5], 16) | int(mac_octets[4], 16) << 8 | int(mac_octets[3], 16) << 16 | ||
ret_mac = ret_mac + 1 | ||
|
||
if (ret_mac & 0xff000000): | ||
print 'Error: increment carries into OUI' | ||
return '' | ||
|
||
mac_octets[5] = hex(ret_mac & 0xff)[2:].zfill(2) | ||
mac_octets[4] = hex((ret_mac >> 8) & 0xff)[2:].zfill(2) | ||
mac_octets[3] = hex((ret_mac >> 16) & 0xff)[2:].zfill(2) | ||
|
||
return ':'.join(mac_octets).upper() | ||
|
||
return '' | ||
|
||
@classmethod | ||
def find_field(cls, e, name): | ||
if not hasattr(cls, 'brd_fmt'): | ||
raise RuntimeError("Class %s does not have brb_fmt" % cls) | ||
if not e: | ||
raise RuntimeError("EEPROM can not be empty") | ||
brd_fmt = cls.brd_fmt | ||
loc = 0 | ||
for f in brd_fmt: | ||
end = loc + f[2] | ||
t = e[loc:end] | ||
loc = end | ||
if f[0] == name: | ||
return t | ||
|
||
def base_mac_addr(self, e): | ||
''' | ||
Returns the base MAC address found in the EEPROM. | ||
Sub-classes must override this method as reading the EEPROM | ||
and finding the base MAC address entails platform specific | ||
details. | ||
See also mgmtaddrstr() and switchaddrstr(). | ||
''' | ||
print "ERROR: Platform did not implement base_mac_addr()" | ||
raise NotImplementedError | ||
|
||
def mgmtaddrstr(self, e): | ||
''' | ||
Returns the base MAC address to use for the Ethernet | ||
management interface(s) on the CPU complex. | ||
By default this is the same as the base MAC address listed in | ||
the EEPROM. | ||
See also switchaddrstr(). | ||
''' | ||
return self.base_mac_addr(e) | ||
|
||
def switchaddrstr(self, e): | ||
''' | ||
Returns the base MAC address to use for the switch ASIC | ||
interfaces. | ||
By default this is *next* address after the base MAC address | ||
listed in the EEPROM. | ||
See also mgmtaddrstr(). | ||
''' | ||
return self.increment_mac(self.base_mac_addr(e)) | ||
|
||
def switchaddrrange(self, e): | ||
# this function is in the base class only to catch errors | ||
# the platform specific import should have an override of this method | ||
# to provide the allocated mac range from syseeprom or flash sector or | ||
# wherever that platform stores this info | ||
print "Platform did not indicate allocated mac address range" | ||
raise NotImplementedError | ||
|
||
def serial_number_str(self, e): | ||
raise NotImplementedError("Platform did not indicate serial number") |
Oops, something went wrong.