예제 #1
0
VOID
CNdasDevice::SetName(LPCTSTR szName)
{
	ximeta::CAutoLock autolock(this);

	HRESULT hr = ::StringCchCopy(
		m_szDeviceName, 
		MAX_NDAS_DEVICE_NAME_LEN, 
		szName);

	_ASSERTE(SUCCEEDED(hr));

	BOOL fSuccess = _NdasSystemCfg.SetValueEx(
		m_szCfgContainer, 
		TEXT("DeviceName"), 
		szName);

	if (!fSuccess) {
		DBGPRT_WARN_EX(
			_FT("Writing device name entry to the registry failed at %s.\n"), 
			m_szCfgContainer);
	}

	CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();
	(VOID) pEventPublisher->DevicePropertyChanged(m_dwSlotNo);
}
예제 #2
0
BOOL 
CNdasDeviceRegistrar::Unregister(const NDAS_DEVICE_ID& DeviceId)
{
	ximeta::CAutoLock autolock(this);

	CNdasDeviceId cdevid(DeviceId);
	DPInfo(_FT("Unregister device %s\n"), (LPCTSTR)cdevid);

	DeviceIdMap::iterator itrId = m_deviceIdMap.find(DeviceId);
	if (m_deviceIdMap.end() == itrId) {
		// TODO: ::SetLastError(NDAS_ERROR_DEVICE_NOT_FOUND);
		// TODO: Make more specific error code
		::SetLastError(NDASHLPSVC_ERROR_DEVICE_ENTRY_NOT_FOUND);
	}

	PCNdasDevice pDevice = itrId->second;
	
	if (pDevice->GetStatus() != NDAS_DEVICE_STATUS_DISABLED) {
		// TODO: ::SetLastError(NDAS_ERROR_CANNOT_UNREGISTER_ENABLED_DEVICE);
		// TODO: Make more specific error code
		::SetLastError(NDASHLPSVC_ERROR_CANNOT_UNREGISTER_ENABLED_DEVICE);
		return FALSE;
	}

	DWORD dwSlotNo = pDevice->GetSlotNo();
	
	_ASSERT(0 != dwSlotNo);

	DeviceSlotMap::iterator itrSlot = m_deviceSlotMap.find(dwSlotNo);

	m_deviceIdMap.erase(itrId);

	m_deviceSlotMap.erase(itrSlot);
	m_pbSlotOccupied[dwSlotNo] = FALSE;

	pDevice->Release();

	TCHAR szContainer[30];
	HRESULT hr = ::StringCchPrintf(szContainer, 30, TEXT("Devices\\%04d"), dwSlotNo);
	_ASSERT(SUCCEEDED(hr));

	BOOL fSuccess = _NdasSystemCfg.DeleteContainer(szContainer, TRUE);
	
	if (!fSuccess) {
		DPWarningEx(
			_FT("Deleting registration entry from the registry failed at %s.\n"), 
			szContainer);
	}

	CNdasInstanceManager* pInstMan = CNdasInstanceManager::Instance();
	_ASSERTE(NULL != pInstMan);

	CNdasEventPublisher* pEventPublisher = pInstMan->GetEventPublisher();
	_ASSERTE(NULL != pEventPublisher);

	(void) pEventPublisher->DeviceEntryChanged();

	return TRUE;
}
예제 #3
0
VOID
CNdasDevice::SetName(LPCTSTR szName)
{
	ximeta::CAutoLock autolock(this);

	HRESULT hr = ::StringCchCopy(
		m_szDeviceName, 
		MAX_NDAS_DEVICE_NAME_LEN, 
		szName);

	_ASSERTE(SUCCEEDED(hr));

	(VOID) SetConfigValue(_T("DeviceName"), szName);

	CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();
	(VOID) pEventPublisher->DevicePropertyChanged(m_dwSlotNo);
}
예제 #4
0
BOOL CNdasEventMonitor::OnLogicalDeviceDisconnected(DWORD nWaitIndex)
{
	ximeta::CAutoLock autolock(this);

	PCNdasLogicalDevice pLogDevice = NULL;

	if (nWaitIndex < m_vLogDevices.size()) {
		pLogDevice = m_vLogDevices[nWaitIndex];
	} else {
		_ASSERTE(FALSE);
		return TRUE;
	}

	if (NULL == pLogDevice) {
		_ASSERTE(FALSE);
		return TRUE;
	}

	CNdasScsiLocation location = pLogDevice->GetNdasScsiLocation();

	DPInfo(_FT("Disconnect Event from %s: %s\n"),
		location.ToString(),
		pLogDevice->ToString());

	//
	// reset the event to prevent consecutive same event pulse
	//
	BOOL fSuccess = ::ResetEvent(pLogDevice->GetDisconnectEvent());
	_ASSERTE(fSuccess);

	CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();

	NDAS_LOGICALDEVICE_ID logicalDeviceId = pLogDevice->GetLogicalDeviceId();

	(VOID) pEventPublisher->LogicalDeviceDisconnected(logicalDeviceId);

	pLogDevice->OnDisconnected();

	return TRUE;
}
예제 #5
0
BOOL 
CNdasEventMonitor::OnLogicalDeviceAlarmed(DWORD nWaitIndex)
{
	ximeta::CAutoLock autolock(this);

	PCNdasLogicalDevice pLogDevice = NULL;

	if (nWaitIndex < m_vLogDevices.size()) {
		pLogDevice = m_vLogDevices[nWaitIndex];
	} else {
		_ASSERTE(FALSE);
		return FALSE;
	}

	if (NULL == pLogDevice) {
		_ASSERTE(FALSE);
		return FALSE;
	}

	CNdasScsiLocation ndasScsiLocation = pLogDevice->GetNdasScsiLocation();

	DPInfo(_FT("Alarm Event from %s: %s\n"),
		ndasScsiLocation.ToString(),
		pLogDevice->ToString());

	if (ndasScsiLocation.IsInvalid()) {
		DBGPRT_ERR(_FT("Invalid SCSI Location\n"));
		_ASSERTE(FALSE);
		return FALSE;
	}

	//
	// reset the event to prevent consecutive same event pulse
	//
	// should return TRUE
	BOOL fSuccess = ::ResetEvent(pLogDevice->GetAlarmEvent());
	_ASSERTE(fSuccess);

	ULONG ulAdapterStatus;

	fSuccess = ::LsBusCtlQueryStatus(
		ndasScsiLocation.SlotNo,
		&ulAdapterStatus);

	if (!fSuccess) {
		DPErrorEx(_FT("Unable to get alarm status, Ignored: "));
		return TRUE;
	}

	if(pLogDevice->GetAdapterStatus() != ulAdapterStatus)
	{
		pLogDevice->SetAdapterStatus(ulAdapterStatus);

		CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();

		(VOID) pEventPublisher->LogicalDeviceAlarmed(pLogDevice->GetLogicalDeviceId(), ulAdapterStatus);
	}

	if(ADAPTERINFO_ISSTATUSFLAG(ulAdapterStatus, ADAPTERINFO_STATUSFLAG_MEMBER_FAULT))
		pLogDevice->SetAllUnitDevicesFault();

	return TRUE;
}
예제 #6
0
//
// Remove the unit device ID from the list
//
BOOL
CNdasLogicalDevice::RemoveUnitDevice(CNdasUnitDevice& unitDevice)
{
	ximeta::CAutoLock autolock(this);
	DWORD ldSequence = unitDevice.GetLDSequence();

	_ASSERTE(ldSequence < m_logicalDeviceGroup.nUnitDevices);
	if (ldSequence >= m_logicalDeviceGroup.nUnitDevices) {
		DBGPRT_ERR(_FT("Invalid sequence (%d) of the unit device.\n"), ldSequence);
		return FALSE;
	}

	_ASSERTE(&unitDevice == m_pUnitDevices[ldSequence]);
	if (&unitDevice != m_pUnitDevices[ldSequence]) {
		DBGPRT_ERR(_FT("Unit device in sequence (%d) is not occupied.\n"), ldSequence);
		return FALSE;
	}

	m_pUnitDevices[ldSequence]->Release();
	m_pUnitDevices[ldSequence] = NULL;

	//
	// Workaround for Fault-tolerant Mode
	// -> Even if the logical device is incomplete,
	//    Mounted Logical Device Location should be intact
	//    if it is mounted
	//
	if (NDAS_LOGICALDEVICE_STATUS_MOUNT_PENDING == GetStatus() ||
		NDAS_LOGICALDEVICE_STATUS_MOUNTED == GetStatus() ||
		NDAS_LOGICALDEVICE_STATUS_UNMOUNT_PENDING == GetStatus()) 
	{
		--m_nUnitDeviceInstances;
		return TRUE;
	}

	if (IsComplete()) {
		CNdasLogicalDeviceManager* pLdm = pGetNdasLogicalDeviceManager();
		pLdm->UnregisterNdasScsiLocation(m_NdasScsiLocation);
	}

	--m_nUnitDeviceInstances;

	//
	// Deallocation NDAS SCSI Location when the fist unit device is removed
	//
	if (0 == ldSequence) {
		cpDeallocateNdasScsiLocation();
	}

	//
	// Publish Event
	//
	CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();
	(VOID) pEventPublisher->LogicalDeviceRelationChanged(m_logicalDeviceId);

	//
	// Set Device Error
	//
	SetLastDeviceError(NDAS_LOGICALDEVICE_ERROR_MISSING_MEMBER);

	return TRUE;
}
예제 #7
0
//
// Set the unit device ID at a sequence 
// to a unit device member ID list
//
BOOL 
CNdasLogicalDevice::AddUnitDevice(CNdasUnitDevice& unitDevice)
{
	ximeta::CAutoLock autolock(this);
	BOOL fSuccess;

	DWORD ldSequence = unitDevice.GetLDSequence();

	_ASSERTE(ldSequence < m_logicalDeviceGroup.nUnitDevices);
	if (ldSequence >= m_logicalDeviceGroup.nUnitDevices) {
		DBGPRT_ERR(_FT("Invalid sequence (%d) of the unit device.\n"), ldSequence);
		return FALSE;
	}

	_ASSERTE(NULL == m_pUnitDevices[ldSequence]);
	if (NULL != m_pUnitDevices[ldSequence]) {
		DBGPRT_ERR(_FT("Sequence (%d) is already occupied.\n"), ldSequence);
		return FALSE;
	}

	_ASSERTE(NULL == m_pUnitDevices[ldSequence]);
	m_pUnitDevices[ldSequence] = &unitDevice;
	m_pUnitDevices[ldSequence]->AddRef();
	++m_nUnitDeviceInstances;

	_ASSERTE(
		m_nUnitDeviceInstances >= 0 && 
		m_nUnitDeviceInstances <= GetUnitDeviceCount()
		);

	if (m_nUnitDeviceInstances < GetUnitDeviceCount()) {
		SetLastDeviceError(NDAS_LOGICALDEVICE_ERROR_MISSING_MEMBER);
	} else {
		SetLastDeviceError(NDAS_LOGICALDEVICE_ERROR_NONE);
	}

	//
	// Allocate NDAS SCSI Location when the first unit device is
	// available
	//
	if (0 == ldSequence) {
		cpAllocateNdasScsiLocation();
	}

	if (IsComplete()) {
		CNdasLogicalDeviceManager* pLdm = pGetNdasLogicalDeviceManager();
		pLdm->RegisterNdasScsiLocation(m_NdasScsiLocation, *this);
	}

	if (IsComplete()) 
	{
		//
		// Reconciliation Support
		//
		BOOL bAlive, bAdapterError;
		fSuccess = ::LsBusCtlQueryNodeAlive(
			m_NdasScsiLocation.SlotNo, 
			&bAlive, 
			&bAdapterError);

		//
		// Reconciliation
		//
		if (fSuccess && bAlive)
		{
			ReconcileFromNdasBus();
		}
	}
	else if (IsComplete() && m_fMountOnReady) 
	{
		//
		// Boot-time Plug In Support
		//

		if ((m_mountOnReadyAccess & GetAllowingAccess()) == 
			m_mountOnReadyAccess)
		{

			(VOID) PlugIn(m_mountOnReadyAccess);

		} else if ((GENERIC_READ & GetAllowingAccess()) && 
			m_fReducedMountOnReadyAccess) 
		{

			(VOID) PlugIn(GENERIC_READ);

		}
	}

	DBGPRT_INFO(_FT("Added %s to Logical Device %s\n"), unitDevice.ToString(), this->ToString());

	CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();
	(VOID) pEventPublisher->LogicalDeviceRelationChanged(m_logicalDeviceId);

	return TRUE;
}
예제 #8
0
BOOL 
CNdasEventMonitor::OnLogicalDeviceAlarmed(DWORD nWaitIndex)
{
	ximeta::CAutoLock autolock(this);

	CNdasLogicalDevice* pLogDevice = NULL;

	if (nWaitIndex < m_vLogDevices.size()) {
		pLogDevice = m_vLogDevices[nWaitIndex];
	} else {
		_ASSERTE(FALSE);
		return FALSE;
	}

	if (NULL == pLogDevice) {
		_ASSERTE(FALSE);
		return FALSE;
	}

	CNdasScsiLocation ndasScsiLocation = pLogDevice->GetNdasScsiLocation();

	DBGPRT_INFO(_FT("Alarm Event from %s: %s\n"),
		ndasScsiLocation.ToString(),
		pLogDevice->ToString());

	if (ndasScsiLocation.IsInvalid()) {
		DBGPRT_ERR(_FT("Invalid SCSI Location\n"));
		_ASSERTE(FALSE);
		return FALSE;
	}

	//
	// reset the event to prevent consecutive same event pulse
	//
	// should return TRUE
	BOOL fSuccess = ::ResetEvent(pLogDevice->GetAlarmEvent());
	_ASSERTE(fSuccess);

	ULONG ulAdapterStatus;

	fSuccess = ::LsBusCtlQueryStatus(
		ndasScsiLocation.SlotNo,
		&ulAdapterStatus);

	if (!fSuccess) {
		DBGPRT_ERR_EX(_FT("(%s) query status failed on alarm, ignored: "), 
			ndasScsiLocation.ToString());
		return TRUE;
	}

	DBGPRT_INFO(_FT("(%s) alarmed %08X.\n"), 
		ndasScsiLocation.ToString(), ulAdapterStatus);

	// Determine whether an alarm will be issued.
	// Only viable alarm will be published
	if(pIsViableAlarmStatus(pLogDevice->GetAdapterStatus(), ulAdapterStatus))
	{
		CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();
		(VOID) pEventPublisher->LogicalDeviceAlarmed(
			pLogDevice->GetLogicalDeviceId(), pGetSignificatAlarm(pLogDevice->GetAdapterStatus(), ulAdapterStatus));
	}

	pLogDevice->SetAdapterStatus(ulAdapterStatus);

	return TRUE;
}
예제 #9
0
DWORD
CNdasEventMonitor::
OnTaskStart()
{
	
	BOOL bTerminateThread(FALSE);

	// 15 sec = 10,000,000 nanosec
	// negative value means relative time
	LARGE_INTEGER liDueTime;
	liDueTime.QuadPart = - 10 * 1000 * 1000;

	BOOL fSuccess = ::SetWaitableTimer(
		m_hHeartbeatMonitorTimer, 
		&liDueTime, 
		HEARTBEAT_MONITOR_INTERVAL, 
		NULL, 
		NULL, 
		FALSE);

	if (!fSuccess) {
		DPErrorEx(_FT("Setting waitable timer failed: "));
	}

	do {

		//
		// Lock against m_vLogDevice set change
		//
		this->Lock();

		DWORD dwLogDevices = m_vLogDevices.size();
		DWORD dwHandles = 3 + 2 * dwLogDevices;
		HANDLE* hWaitingHandles = new HANDLE[dwHandles];

		hWaitingHandles[0] = m_hTaskTerminateEvent;
		hWaitingHandles[1] = m_hLogDeviceSetChangeEvent;
		hWaitingHandles[2] = m_hHeartbeatMonitorTimer;

		PCNdasLogicalDeviceVector::const_iterator itr = m_vLogDevices.begin();
		for (DWORD i = 3; itr != m_vLogDevices.end(); ++itr, ++i) {
			PCNdasLogicalDevice pLogDevice = *itr;
			hWaitingHandles[i] = pLogDevice->GetDisconnectEvent();
			hWaitingHandles[dwLogDevices + i] = pLogDevice->GetAlarmEvent();
		}
		this->Unlock();

		BOOL fSuccess = ::ResetEvent(m_hLogDeviceSetChangeEvent);
		_ASSERTE(fSuccess);

		BOOL bResetLogDeviceSet(FALSE);

		do {

			DWORD dwWaitResult = ::WaitForMultipleObjects(
				dwHandles, 
				hWaitingHandles, 
				FALSE, 
				INFINITE);

			if (WAIT_OBJECT_0 == dwWaitResult) {
				//
				// Terminate Thread Event
				//
				bTerminateThread = TRUE;
			} else if (WAIT_OBJECT_0 + 1 == dwWaitResult) {
				//
				// LogicalDeviceSetChange Event
				//
				bResetLogDeviceSet = TRUE;
			} else if (WAIT_OBJECT_0 + 2 == dwWaitResult) {
				//
				// Heartbeat Monitor Timer Event
				//
				ximeta::CAutoLock autolock(this);
				PCNdasDeviceSet::const_iterator itr = m_hbMonDevices.begin();
				m_bIterating = TRUE;
				for (; itr != m_hbMonDevices.end();) {
					PCNdasDevice pDevice = *itr;
					BOOL bDetach = pDevice->OnStatusCheck();
					if (bDetach) {
						DPInfo(_FT("Detaching device %s from the monitor\n"),
							CNdasDeviceId(pDevice->GetDeviceId()).ToString());
						itr = m_hbMonDevices.erase(itr);
					} else {
						++itr;
					}
				}
				m_bIterating = FALSE;

			} else if (WAIT_OBJECT_0 + 3 <= dwWaitResult &&
				dwWaitResult < WAIT_OBJECT_0 + 3 + dwLogDevices)
			{
				//
				// Disconnect Event
				//
				DWORD n = dwWaitResult - (WAIT_OBJECT_0 + 3);
				PCNdasLogicalDevice pLogDevice = m_vLogDevices[n];

				DPInfo(_FT("Disconnect Event from slot %d: %s\n"),
					pLogDevice->GetSlot(), 
					pLogDevice->ToString());

				//
				// Publish event here
				//
				// TODO: Publish event here
				//
				CNdasInstanceManager* pInstMan = CNdasInstanceManager::Instance();
				_ASSERTE(NULL != pInstMan);

				CNdasEventPublisher* pEventPublisher = pInstMan->GetEventPublisher();
				_ASSERTE(NULL != pEventPublisher);

				NDAS_LOGICALDEVICE_ID logicalDeviceId = {0};
				logicalDeviceId.SlotNo = pLogDevice->GetSlot();
				logicalDeviceId.TargetId = 0;
				logicalDeviceId.LUN = 0;
				(void) pEventPublisher->LogicalDeviceDisconnected(logicalDeviceId);

				BOOL fSuccess = ::ResetEvent(pLogDevice->GetDisconnectEvent());
				_ASSERTE(fSuccess);

				//
				// Eject device
				//
				DPInfo(_FT("Ejecting disconnected logical device\n"));

				fSuccess = pLogDevice->Eject();
				if (!fSuccess) {
					DPErrorEx(_FT("Eject failed: "));
					DPError(_FT("Trying to unplug...\n"));
					fSuccess = pLogDevice->Unplug();
					if (!fSuccess) {
						DPErrorEx(_FT("Unplugging failed: "));
					}
				}

			} else if (WAIT_OBJECT_0 + 3 + dwLogDevices <= dwWaitResult &&
				dwWaitResult < WAIT_OBJECT_0 + 3 + 2 * dwLogDevices)
			{
				//
				// Alarm Event
				//
				DWORD n = dwWaitResult - (WAIT_OBJECT_0 + 3 + dwLogDevices);
				PCNdasLogicalDevice pLogDevice = m_vLogDevices[n];
				ULONG ulStatus;

				DPInfo(_FT("Alarm Event from slot %d: %s\n"),
					pLogDevice->GetSlot(),
					pLogDevice->ToString());

				LsBusCtlQueryAlarmStatus(pLogDevice->GetSlot(), &ulStatus);
				
				CNdasInstanceManager* pInstMan = CNdasInstanceManager::Instance();
				_ASSERTE(NULL != pInstMan);

				CNdasEventPublisher* pEventPublisher = pInstMan->GetEventPublisher();
				_ASSERTE(NULL != pEventPublisher);

				switch (ulStatus) {
				case ALARM_STATUS_NORMAL:
					{
						DPInfo(_FT("Alarm Normal\n"));
						NDAS_LOGICALDEVICE_ID logicalDeviceId = {0};
						logicalDeviceId.SlotNo = pLogDevice->GetSlot();
						logicalDeviceId.TargetId = 0;
						logicalDeviceId.LUN = 0;
						(void) pEventPublisher->
							LogicalDeviceReconnected(logicalDeviceId);
					}

					break;
				case ALARM_STATUS_START_RECONNECT:
					{
						DPInfo(_FT("Alarm Start Reconnect\n"));
						NDAS_LOGICALDEVICE_ID logicalDeviceId = {0};
						logicalDeviceId.SlotNo = pLogDevice->GetSlot();
						logicalDeviceId.TargetId = 0;
						logicalDeviceId.LUN = 0;
						(void) pEventPublisher->
							LogicalDeviceReconnecting(logicalDeviceId);
					}
					break;
				case ALARM_STATUS_FAIL_RECONNECT: // obsolete
					DPInfo(_FT("Alarm Failed Reconnecting\n"));
					break;
				default:
					DPWarning(_FT("Unknown alarm status: %d\n"), ulStatus);
				}

				//
				// TODO: Publish event here
				//

				BOOL fSuccess = ::ResetEvent(pLogDevice->GetAlarmEvent());
				_ASSERTE(fSuccess);

			} else {
				_ASSERTE(FALSE);
				DPErrorEx(_FT("Wait failed: "));
			}

		} while (!bResetLogDeviceSet && !bTerminateThread);

		delete [] hWaitingHandles;

	} while (!bTerminateThread);

	fSuccess = ::CancelWaitableTimer(m_hHeartbeatMonitorTimer);
	if (!fSuccess) {
		DPErrorEx(_FT("Canceling waitable timer failed: "));
	}

	return 0;
}
예제 #10
0
PCNdasDevice
CNdasDeviceRegistrar::Register(const NDAS_DEVICE_ID& DeviceId, DWORD dwSlotNo)
{
	//
	// this will lock this class from here
	// and releases the lock when the function returns;
	//
	ximeta::CAutoLock autolock(this);
	
	DPInfo(_FT("Registering device %s at slot %d\n"), 
		LPCTSTR(CNdasDeviceId(DeviceId)), dwSlotNo);

	// check slot number
	if (dwSlotNo < 1 || dwSlotNo > m_dwMaxSlotNo) {
		::SetLastError(NDASHLPSVC_ERROR_INVALID_SLOT_NUMBER);
		return NULL;
	}

	// check and see if the slot is occupied
	if (m_pbSlotOccupied[dwSlotNo]) {
		::SetLastError(NDASHLPSVC_ERROR_SLOT_ALREADY_OCCUPIED);
		return NULL;
	}

	// find an duplicate address
	if (NULL != Find(DeviceId)) {
		::SetLastError(NDASHLPSVC_ERROR_DUPLICATE_DEVICE_ENTRY);
		return NULL;
	}

	// register
	PCNdasDevice pDevice = new CNdasDevice(dwSlotNo, DeviceId);
	if (NULL == pDevice) {
		// memory allocation failed
		// No need to set error here!
		return NULL;
	}
	pDevice->AddRef();

	BOOL fSuccess = pDevice->Initialize();
	if (!fSuccess) {
//		DebugPrintError((ERROR_T("Device initialization failed!")));
		pDevice->Release();
		return NULL;
	}

	m_pbSlotOccupied[dwSlotNo] = TRUE;

	bool insertResult;

	insertResult = 
		m_deviceSlotMap.insert(DeviceSlotMap::value_type(dwSlotNo, pDevice)).second;
	_ASSERTE(insertResult == true);
	insertResult =	
		m_deviceIdMap.insert(DeviceIdMap::value_type(DeviceId, pDevice)).second;
	_ASSERTE(insertResult == true);

	_ASSERTE(m_deviceSlotMap.size() == m_deviceIdMap.size());

	TCHAR szContainer[30];
	HRESULT hr = ::StringCchPrintf(szContainer, 30, TEXT("Devices\\%04d"), dwSlotNo);
	_ASSERT(SUCCEEDED(hr));

	fSuccess = _NdasSystemCfg.SetSecureValueEx(
		szContainer, 
		_T("DeviceId"), 
		&DeviceId, 
		sizeof(DeviceId));

	if (!fSuccess) {
		DPWarningEx(
			_FT("Writing registration entry to the registry failed at %s.\n"), 
			szContainer);
	}

	//
	// During bootstrapping, we do not publish this event
	// Bootstrapper will do this later.
	//
	if (!m_fBootstrapping) {
		CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();
		(VOID) pEventPublisher->DeviceEntryChanged();
	}

	return pDevice;
}