Ryan Malloy a7f62e5d7e Add Silicon Labs CP210x manufacturing library source
- Library source from AN721SW (cp210xmanufacturing_1.0.tar.gz)
- Manufacturing tool source (cp210xsmt)
- Builds 64-bit shared library with: make LIB_ARCH=64
- Datasheets and app notes in docs/
2026-01-30 10:31:39 -07:00

541 lines
18 KiB
C++

// Copyright (c) 2015-2016 by Silicon Laboratories Inc. All rights reserved.
// The program contained in this listing is proprietary to Silicon Laboratories,
// headquartered in Austin, Texas, U.S.A. and is subject to worldwide copyright
// protection, including protection under the United States Copyright Act of 1976
// as an unpublished work, pursuant to Section 104 and Section 408 of Title XVII
// of the United States code. Unauthorized copying, adaptation, distribution,
// use, or display is prohibited by this law.
#ifndef __SLABSMT_H__
#define __SLABSMT_H__ 1
#ifdef _WIN32
#include <Windows.h>
#pragma warning(disable : 4996) // TODO - I'd be happy to use never functions if everybody upgraded to VS2015
#else
#include "OsDep.h"
#endif
#include <errno.h> // for errno
#include "stdio.h"
inline void delayMsec( DWORD msec)
{
Sleep( msec);
}
struct CErrMsg
{
CErrMsg( const char *msg) : m_Msg( msg) {}
CErrMsg( const std::string msg ) : m_Msg( msg) {}
const std::string &msg() const { return m_Msg; }
private:
const std::string m_Msg;
};
struct CDllErr : public CErrMsg // thrown any time a call to the DLL fails
{
CDllErr( const char *msg) : CErrMsg( msg) {}
};
struct CCustErr : public CErrMsg // thrown any time the customization process goes wrong
{
CCustErr( const char *msg) : CErrMsg( msg) {}
};
struct CUsageErr : public CErrMsg // thrown any time a parameter is specified wrong
{
CUsageErr( const std::string msg ) : CErrMsg( msg) {}
};
// Just to save 3 lines each time this needs to be done
inline void setSpecified( bool &specified, const std::string &parmName)
{
if( specified)
{
throw CSyntErr( std::string( "multiple occurence of ") + parmName);
}
specified = true;
}
//-----------------------------------------------------------------------
// ctor reads SNs from command line or auto-generates if command line says so
struct CSerNumSet
{
// analyzes the command line; if requested, creates a set of SNs
CSerNumSet( int argc, const char * argv[], bool mayAutoGen, DWORD requiredCnt);
void write() const;
size_t size() const { return m_SN.size(); }
bool empty() const { return m_SN.empty(); }
const std::vector< BYTE>& at( DWORD index) const { return m_SN[ index ]; }
// if the set contains the gives SN, removes it and returns true
bool findAndErase( const std::vector< BYTE> &sN);
private:
void assertUniquness() const;
bool m_AreNeeded;
std::vector< std::vector< BYTE> > m_SN;
};
//-----------------------------------------------------------------------
// check if one of command line arguments is equal to the string
bool isSpecified( int argc, const char * argv[], const std::string &parmName);
// find a command line argument equal to the string and convert the next one to DWORD, throw CUsageErr otherwise
DWORD decimalParm( int argc, const char * argv[], const std::string &parmName);
//-----------------------------------------------------------------------
// Identifies a specific device within a family of devices supported by the same customization lib.
struct CDevType
{
CDevType( BYTE partNum) : m_PartNum( partNum) {}
BYTE Value() const { return m_PartNum; }
private:
const BYTE m_PartNum;
};
//-----------------------------------------------------------------------
struct CVidPid
{
CVidPid( WORD vid, WORD pid) : m_Vid( vid), m_Pid( pid ) {}
const WORD m_Vid;
const WORD m_Pid;
};
//-----------------------------------------------------------------------
// These functions must be implemented in the library-specific module
//
DWORD LibSpecificNumDevices( const CVidPid &oldVidPid, const CVidPid &newVidPid);
// This func must call the templated DevSpecificMain with device-specific types
void LibSpecificMain( const CDevType &devType, const CVidPid &vidPid, int argc, const char * argv[]);
//---------------------------------------------------------------------------------
// A helper class for CDevSet, to associate a dtor with the vector of device pointers, that deletes
// all devices. This can't be done in CDevSet dtor because it won't be called if ctor throws.
// If I could use C++ 11 and emplace_back() were not broken in VS2015, I'd simply use a vector of objects
// instead of vectror of pointers.
template< class TDev >
struct CDevVector : public std::vector< TDev*>
{
~CDevVector()
{
for( size_t i = 0; i < std::vector< TDev*>::size(); i++)
{
delete std::vector<TDev*>::at( i);
}
}
};
//---------------------------------------------------------------------------------
// Set of opened devices. Not all devices exposed by the
// customization lib are included, but only those matching FilterVidPid.
template< class TDev >
class CDevSet
{
public:
CDevSet( const CDevType &FilterDevType, const CVidPid &FilterVidPid, bool allowLocked = false);
DWORD size() const { return static_cast<DWORD>( m_DevSet.size()); }
const TDev& at( size_t i) const { return *m_DevSet[ i]; }
void printDevInfo() const;
private:
CDevVector<TDev> m_DevSet;
};
template< class TDev >
CDevSet<TDev>::CDevSet( const CDevType &FilterDevType, const CVidPid &FilterVidPid, bool allowLocked)
{
DWORD NumDevs = LibSpecificNumDevices( FilterVidPid, FilterVidPid);
ASSERT( m_DevSet.empty());
for( DWORD i = 0; i < NumDevs; i++)
{
TDev *pDev = new TDev( FilterVidPid, i);
const CDevType devType = pDev->getDevType();
const CVidPid vidPid = pDev->getVidPid();
if( FilterDevType.Value() == devType.Value() &&
( FilterVidPid.m_Vid == vidPid.m_Vid) &&
( FilterVidPid.m_Pid == vidPid.m_Pid))
{
if( pDev->isLocked() && !allowLocked)
{
delete pDev;
throw CCustErr( "Locked device found");
}
m_DevSet.push_back( pDev);
}
else
{
delete pDev;
}
}
}
template< class TDev >
void CDevSet<TDev>::printDevInfo() const
{
for( size_t i = 0; i < size(); i++)
{
const CVidPid vidPid = at( i).getVidPid();
printf( "VID: %04x PID: %04x ", vidPid.m_Vid, vidPid.m_Pid);
printf( "Prod Str: %s ", toString( at( i).getProduct( true)).c_str());
printf( "Ser #: %s", toString( at( i).getSerNum( true)).c_str());
printf( "\n");
}
}
//---------------------------------------------------------------------------------
// Manufacturer string customization availability varies by device, so it's extracted into a class
// of its own, then included by each individual device type that supports it.
template< class TDev >
struct CManufacturerString
{
CManufacturerString( bool supportsUnicode) : m_SupportsUnicode( supportsUnicode) { m_Specified = false; }
bool readParm( const std::string &parmName);
void program( const TDev &dev) const;
void verify( const TDev &dev) const;
private:
const bool m_SupportsUnicode;
bool m_Specified;
bool m_IsAscii;
std::vector<BYTE> m_str;
};
template< class TDev >
bool CManufacturerString<TDev>::readParm( const std::string &parmName)
{
if( parmName == "ManufacturerStringAscii")
{
setSpecified( m_Specified, parmName);
m_IsAscii = true;
readByteArrayParm( m_str, MAX_UCHAR);
readKeyword( "}"); // end of parameter list
return true;
}
else if( parmName == "ManufacturerStringUnicode" && m_SupportsUnicode)
{
setSpecified( m_Specified, parmName);
m_IsAscii = false;
readByteArrayParm( m_str, MAX_UCHAR);
readKeyword( "}"); // end of parameter list
return true;
}
return false;
}
template< class TDev >
void CManufacturerString<TDev>::program( const TDev &dev) const
{
if( !m_Specified) { return; }
dev.setManufacturer( m_str, m_IsAscii);
}
template< class TDev >
void CManufacturerString<TDev>::verify( const TDev &dev) const
{
if( !m_Specified) { return; }
if( m_str != dev.getManufacturer( m_IsAscii))
{
throw CCustErr( "Failed ManufacturerString verification");
}
}
//---------------------------------------------------------------------------------
// Common parameters for all devices
// WARNING: program() doesn't program all items readParm() and verify() handle.
// The rest of them is left to device-specific Parms::program().
template< class TDev >
struct CDevParms
{
CDevParms()
{
m_VidPidSpecified = false;
m_ProdStrSpecified = false;
m_PowerModeSpecified = false;
m_MaxPowerSpecified = false;
m_DevVerSpecified = false;
}
void read()
{
std::string parmName;
while( readWord( parmName))
{
readKeyword( "{"); // start of parameter list
readParm( parmName);
}
}
virtual bool supportsUnicode() const { return false; }
virtual void readParm( const std::string &parmName);
virtual void program( const TDev &dev, const std::vector<BYTE> *pSerNum) const;
virtual void verify( const TDev &dev, CSerNumSet &serNumSet) const;
bool m_VidPidSpecified;
WORD m_Vid;
WORD m_Pid;
bool m_ProdStrSpecified;
bool m_ProdStrIsAscii;
std::vector<BYTE> m_ProdStr;
bool m_PowerModeSpecified;
BYTE m_PowerMode;
bool m_MaxPowerSpecified;
BYTE m_MaxPower;
bool m_DevVerSpecified;
WORD m_DevVer;
void resetAll( const CDevSet<TDev> &devSet) const;
void programAll( const CDevSet<TDev> &devSet, const CSerNumSet &serNumSet) const;
void verifyAll( const CDevSet<TDev> &devSet, CSerNumSet sSerNumSet) const;
void lockAll( const CDevSet<TDev> &devSet) const;
};
void waitForTotalDeviceCount( const CVidPid &oldVidPid, const CVidPid &newVidPid, DWORD expectedCount);
template< class TDev >
void CDevParms<TDev>::readParm( const std::string &parmName)
{
if( parmName == "VidPid")
{
setSpecified( m_VidPidSpecified, parmName);
m_Vid = readUshortParm();
m_Pid = readUshortParm();
readKeyword( "}"); // end of parameter list
}
else if( parmName == "PowerMode")
{
setSpecified( m_PowerModeSpecified, parmName);
m_PowerMode = readUcharParm();
readKeyword( "}"); // end of parameter list
}
else if( parmName == "MaxPower")
{
setSpecified( m_MaxPowerSpecified, parmName);
m_MaxPower = readUcharParm();
readKeyword( "}"); // end of parameter list
}
else if( parmName == "DeviceVersion")
{
setSpecified( m_DevVerSpecified, parmName);
m_DevVer = readUshortParm();
readKeyword( "}"); // end of parameter list
}
else if( parmName == "ProductStringAscii")
{
setSpecified( m_ProdStrSpecified, parmName);
m_ProdStrIsAscii = true;
readByteArrayParm( m_ProdStr, MAX_UCHAR);
readKeyword( "}"); // end of parameter list
}
else if( parmName == "ProductStringUnicode" && supportsUnicode())
{
setSpecified( m_ProdStrSpecified, parmName);
m_ProdStrIsAscii = false;
readByteArrayParm( m_ProdStr, MAX_UCHAR);
readKeyword( "}"); // end of parameter list
}
else
{
throw CSyntErr( std::string( "unknown customization parameter ") + parmName);
}
}
template< class TDev >
void CDevParms<TDev>::program( const TDev &dev, const std::vector<BYTE> *pSerNum) const
{
if( pSerNum)
{
dev.setSerNum( *pSerNum, true /*isAscii*/);
}
if( m_ProdStrSpecified)
{
dev.setProduct( m_ProdStr, m_ProdStrIsAscii);
}
}
template< class TDev >
void CDevParms<TDev>::verify( const TDev &dev, CSerNumSet &serNumSet) const
{
if( !serNumSet.empty())
{
if( !serNumSet.findAndErase( dev.getSerNum( true /*isAscii*/)))
{
throw CCustErr( "Failed serial number verification");
}
}
if( m_VidPidSpecified)
{
CVidPid devVidPid = dev.getVidPid();
if( devVidPid.m_Vid != m_Vid || devVidPid.m_Pid != m_Pid)
{
throw CCustErr( "Failed VidPid verification");
}
}
if( m_PowerModeSpecified)
{
if( m_PowerMode != dev.getPowerMode())
{
throw CCustErr( "Failed PowerMode verification");
}
}
if( m_MaxPowerSpecified)
{
if( m_MaxPower != dev.getMaxPower())
{
throw CCustErr( "Failed MaxPower verification");
}
}
if( m_DevVerSpecified)
{
if( m_DevVer != dev.getDevVer())
{
throw CCustErr( "Failed DeviceVersion verification");
}
}
if( m_ProdStrSpecified)
{
if( m_ProdStr != dev.getProduct( m_ProdStrIsAscii))
{
throw CCustErr( "Failed ProductString verification");
}
}
}
template< class TDev >
void CDevParms<TDev>::resetAll( const CDevSet<TDev> &devSet) const
{
for( size_t i = 0; i < devSet.size(); i++)
{
devSet.at( i).reset();
}
}
template< class TDev >
void CDevParms<TDev>::programAll( const CDevSet<TDev> &devSet, const CSerNumSet &serNumSet) const
{
for( DWORD i = 0; i < devSet.size(); i++)
{
program( devSet.at( i), !serNumSet.empty() ? &serNumSet.at( i) : NULL);
}
}
template< class TDev >
void CDevParms<TDev>::verifyAll( const CDevSet<TDev> &devSet, CSerNumSet serNumSet) const
{
for( DWORD i = 0; i < devSet.size(); i++)
{
verify( devSet.at( i), serNumSet);
}
ASSERT( serNumSet.empty());
}
template< class TDev >
void CDevParms<TDev>::lockAll( const CDevSet<TDev> &devSet) const
{
for( DWORD i = 0; i < devSet.size(); i++)
{
devSet.at( i).lock();
}
}
//---------------------------------------------------------------------------------
template< class TDev, class TDevParms >
void DevSpecificMain( const CDevType &devType, const CVidPid &FilterVidPid, int argc, const char * argv[])
{
TDevParms devParms;
devParms.read();
// If the cfg file doesn't specify a new vid-pid, it's not changing;
// so the new vid-pid is the same as filter vid-pid.
const CVidPid NewFilterVidPid( devParms.m_VidPidSpecified ? devParms.m_Vid : FilterVidPid.m_Vid,
devParms.m_VidPidSpecified ? devParms.m_Pid : FilterVidPid.m_Pid);
if( isSpecified( argc, argv, "--reset"))
{
const CDevSet<TDev> oldDevSet( devType, FilterVidPid, true /*allowLocked*/);
devParms.resetAll( oldDevSet);
if( FilterVidPid.m_Vid != NewFilterVidPid.m_Vid || FilterVidPid.m_Pid != NewFilterVidPid.m_Pid)
{
const CDevSet<TDev> newDevSet( devType, NewFilterVidPid, true /*allowLocked*/);
devParms.resetAll( newDevSet);
}
return;
}
if( isSpecified( argc, argv, "--list"))
{
const CDevSet<TDev> oldDevSet( devType, FilterVidPid, true /*allowLocked*/);
printf( "--- devices --------------\n");
oldDevSet.printDevInfo();
if( FilterVidPid.m_Vid != NewFilterVidPid.m_Vid || FilterVidPid.m_Pid != NewFilterVidPid.m_Pid)
{
const CDevSet<TDev> newDevSet( devType, NewFilterVidPid, true /*allowLocked*/);
newDevSet.printDevInfo();
}
printf( "--------------------------\n");
return;
}
const bool program = isSpecified( argc, argv, "--set-and-verify-config") ||
isSpecified( argc, argv, "--set-config") ;
const bool verify = isSpecified( argc, argv, "--set-and-verify-config") ||
isSpecified( argc, argv, "--verify-config") ||
isSpecified( argc, argv, "--verify-locked-config") ;
const bool lock = isSpecified( argc, argv, "--lock");
if( lock && !verify)
{
throw CUsageErr( "--lock must be combined with one of \"verify\" commands");
}
const DWORD custNumDevices = decimalParm( argc, argv, "--device-count");
const CSerNumSet serNumSet( argc, argv, program, custNumDevices);
const DWORD startNumDevices = LibSpecificNumDevices( FilterVidPid, FilterVidPid);
if( program)
{
const CDevSet<TDev> devSet( devType, FilterVidPid);
if( devSet.size() != custNumDevices)
{
char msg[ 128];
sprintf( msg, "programming step: expected %d devices, found %d", custNumDevices, devSet.size());
throw CCustErr( msg);
}
serNumSet.write();
devParms.programAll( devSet, serNumSet);
if( verify)
{
devParms.resetAll( devSet);
}
printf( "programmed %u devices: OK\n", devSet.size());
}
if( verify)
{
#if 0
printf( "*** unplug now ***\n");
delayMsec( 2000);
#endif
// retry verification until it succeeds, the user can press ^C to cancel
#ifdef _WIN32
#pragma warning(suppress : 4127)
#endif
while( true)
{
try // catch the exceptions that mignt go away on retry
{
if( program)
{
waitForTotalDeviceCount( FilterVidPid, NewFilterVidPid, startNumDevices);
}
const bool allowLocked = !program && !lock && isSpecified( argc, argv, "--verify-locked-config");
const CDevSet<TDev> devSet( devType, NewFilterVidPid, allowLocked);
if( devSet.size() != custNumDevices)
{
char msg[ 128];
sprintf( msg, "verification step: expected %d devices, found %d", custNumDevices, devSet.size());
throw CCustErr( msg);
}
devParms.verifyAll( devSet, serNumSet); // gets a copy of serNumSet
printf( "verified %d devices: OK\n", devSet.size());
if( lock)
{
devParms.lockAll( devSet);
printf( "locked %d devices: OK\n", devSet.size());
}
break;
}
catch( const CDllErr e)
{
std::cerr << "WARNING: library: " << e.msg() << "\n";
}
catch( const CCustErr e)
{
std::cerr << "WARNING: Manufacturing process: " << e.msg() << "\n";
}
delayMsec( 1000);
std::cerr << "Retrying verification...\n";
}
}
}
#endif // __SLABSMT_H__