mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 00:45:43 +00:00
[NTOSKRNL]
- Change PnpRootCreateDevice's ServiceName parameter to be a full service name so it can be used by IoReportDetectedDevice (for reporting devices without the "LEGACY_" prefix) and change all of its callers accordingly - Remove the broken implementation of IoReportDetectedDevice and replace it with a fully working implementation - There is still a bug that it creates a new device instance every boot, but unlike the previous code, at least it creates one ;-) svn path=/trunk/; revision=46639
This commit is contained in:
parent
4ee89fa8f2
commit
4c9fecffae
3 changed files with 269 additions and 25 deletions
|
@ -46,11 +46,12 @@ typedef struct _INVALIDATE_DEVICE_RELATION_DATA
|
||||||
|
|
||||||
/* FUNCTIONS *****************************************************************/
|
/* FUNCTIONS *****************************************************************/
|
||||||
|
|
||||||
static NTSTATUS
|
NTSTATUS
|
||||||
IopAssignDeviceResources(
|
IopAssignDeviceResources(
|
||||||
IN PDEVICE_NODE DeviceNode,
|
IN PDEVICE_NODE DeviceNode,
|
||||||
OUT ULONG *pRequiredSize);
|
OUT ULONG *pRequiredSize);
|
||||||
static NTSTATUS
|
|
||||||
|
NTSTATUS
|
||||||
IopTranslateDeviceResources(
|
IopTranslateDeviceResources(
|
||||||
IN PDEVICE_NODE DeviceNode,
|
IN PDEVICE_NODE DeviceNode,
|
||||||
IN ULONG RequiredSize);
|
IN ULONG RequiredSize);
|
||||||
|
@ -361,6 +362,8 @@ IopCreateDeviceNode(PDEVICE_NODE ParentNode,
|
||||||
PDEVICE_NODE Node;
|
PDEVICE_NODE Node;
|
||||||
NTSTATUS Status;
|
NTSTATUS Status;
|
||||||
KIRQL OldIrql;
|
KIRQL OldIrql;
|
||||||
|
UNICODE_STRING FullServiceName;
|
||||||
|
UNICODE_STRING LegacyPrefix = RTL_CONSTANT_STRING(L"LEGACY_");
|
||||||
|
|
||||||
DPRINT("ParentNode 0x%p PhysicalDeviceObject 0x%p ServiceName %wZ\n",
|
DPRINT("ParentNode 0x%p PhysicalDeviceObject 0x%p ServiceName %wZ\n",
|
||||||
ParentNode, PhysicalDeviceObject, ServiceName);
|
ParentNode, PhysicalDeviceObject, ServiceName);
|
||||||
|
@ -375,7 +378,22 @@ IopCreateDeviceNode(PDEVICE_NODE ParentNode,
|
||||||
|
|
||||||
if (!PhysicalDeviceObject)
|
if (!PhysicalDeviceObject)
|
||||||
{
|
{
|
||||||
Status = PnpRootCreateDevice(ServiceName, &PhysicalDeviceObject);
|
if (ServiceName)
|
||||||
|
{
|
||||||
|
FullServiceName.MaximumLength = LegacyPrefix.Length + ServiceName->Length;
|
||||||
|
FullServiceName.Length = 0;
|
||||||
|
FullServiceName.Buffer = ExAllocatePool(PagedPool, FullServiceName.MaximumLength);
|
||||||
|
if (!FullServiceName.Buffer)
|
||||||
|
{
|
||||||
|
ExFreePool(Node);
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlAppendUnicodeStringToString(&FullServiceName, &LegacyPrefix);
|
||||||
|
RtlAppendUnicodeStringToString(&FullServiceName, ServiceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = PnpRootCreateDevice(ServiceName ? &FullServiceName : NULL, &PhysicalDeviceObject);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("PnpRootCreateDevice() failed with status 0x%08X\n", Status);
|
DPRINT1("PnpRootCreateDevice() failed with status 0x%08X\n", Status);
|
||||||
|
@ -709,7 +727,6 @@ IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
IopSetDeviceInstanceData(HANDLE InstanceKey,
|
IopSetDeviceInstanceData(HANDLE InstanceKey,
|
||||||
PDEVICE_NODE DeviceNode)
|
PDEVICE_NODE DeviceNode)
|
||||||
|
@ -799,7 +816,7 @@ IopSetDeviceInstanceData(HANDLE InstanceKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static NTSTATUS
|
NTSTATUS
|
||||||
IopAssignDeviceResources(
|
IopAssignDeviceResources(
|
||||||
IN PDEVICE_NODE DeviceNode,
|
IN PDEVICE_NODE DeviceNode,
|
||||||
OUT ULONG *pRequiredSize)
|
OUT ULONG *pRequiredSize)
|
||||||
|
@ -1014,7 +1031,7 @@ ByeBye:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static NTSTATUS
|
NTSTATUS
|
||||||
IopTranslateDeviceResources(
|
IopTranslateDeviceResources(
|
||||||
IN PDEVICE_NODE DeviceNode,
|
IN PDEVICE_NODE DeviceNode,
|
||||||
IN ULONG RequiredSize)
|
IN ULONG RequiredSize)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* COPYRIGHT: GPL - See COPYING in the top level directory
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
||||||
* FILE: ntoskrnl/io/pnpmgr/pnpreport.c
|
* FILE: ntoskrnl/io/pnpmgr/pnpreport.c
|
||||||
* PURPOSE: Device Changes Reporting Functions
|
* PURPOSE: Device Changes Reporting Functions
|
||||||
* PROGRAMMERS: Filip Navara (xnavara@volny.cz)
|
* PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES ******************************************************************/
|
/* INCLUDES ******************************************************************/
|
||||||
|
@ -12,6 +12,90 @@
|
||||||
#define NDEBUG
|
#define NDEBUG
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath,
|
||||||
|
OUT PHANDLE Handle);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
IopAssignDeviceResources(
|
||||||
|
IN PDEVICE_NODE DeviceNode,
|
||||||
|
OUT ULONG *pRequiredSize);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
IopSetDeviceInstanceData(HANDLE InstanceKey,
|
||||||
|
PDEVICE_NODE DeviceNode);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
IopTranslateDeviceResources(
|
||||||
|
IN PDEVICE_NODE DeviceNode,
|
||||||
|
IN ULONG RequiredSize);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
|
||||||
|
PVOID Context);
|
||||||
|
|
||||||
|
/* PRIVATE FUNCTIONS *********************************************************/
|
||||||
|
|
||||||
|
PWCHAR
|
||||||
|
IopGetInterfaceTypeString(INTERFACE_TYPE IfType)
|
||||||
|
{
|
||||||
|
switch (IfType)
|
||||||
|
{
|
||||||
|
case Internal:
|
||||||
|
return L"Internal";
|
||||||
|
|
||||||
|
case Isa:
|
||||||
|
return L"Isa";
|
||||||
|
|
||||||
|
case Eisa:
|
||||||
|
return L"Eisa";
|
||||||
|
|
||||||
|
case MicroChannel:
|
||||||
|
return L"MicroChannel";
|
||||||
|
|
||||||
|
case TurboChannel:
|
||||||
|
return L"TurboChannel";
|
||||||
|
|
||||||
|
case PCIBus:
|
||||||
|
return L"PCIBus";
|
||||||
|
|
||||||
|
case VMEBus:
|
||||||
|
return L"VMEBus";
|
||||||
|
|
||||||
|
case NuBus:
|
||||||
|
return L"NuBus";
|
||||||
|
|
||||||
|
case PCMCIABus:
|
||||||
|
return L"PCMCIABus";
|
||||||
|
|
||||||
|
case CBus:
|
||||||
|
return L"CBus";
|
||||||
|
|
||||||
|
case MPIBus:
|
||||||
|
return L"MPIBus";
|
||||||
|
|
||||||
|
case MPSABus:
|
||||||
|
return L"MPSABus";
|
||||||
|
|
||||||
|
case ProcessorInternal:
|
||||||
|
return L"ProcessorInternal";
|
||||||
|
|
||||||
|
case PNPISABus:
|
||||||
|
return L"PNPISABus";
|
||||||
|
|
||||||
|
case PNPBus:
|
||||||
|
return L"PNPBus";
|
||||||
|
|
||||||
|
case Vmcs:
|
||||||
|
return L"Vmcs";
|
||||||
|
|
||||||
|
default:
|
||||||
|
DPRINT1("Invalid bus type: %d\n", IfType);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* PUBLIC FUNCTIONS **********************************************************/
|
/* PUBLIC FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -30,29 +114,56 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
|
||||||
{
|
{
|
||||||
PDEVICE_NODE DeviceNode;
|
PDEVICE_NODE DeviceNode;
|
||||||
PDEVICE_OBJECT Pdo;
|
PDEVICE_OBJECT Pdo;
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status;
|
||||||
|
HANDLE InstanceKey;
|
||||||
|
ULONG RequiredLength;
|
||||||
|
UNICODE_STRING ValueName, ServiceName;
|
||||||
|
WCHAR HardwareId[256];
|
||||||
|
PWCHAR IfString;
|
||||||
|
ULONG IdLength;
|
||||||
|
|
||||||
DPRINT("__FUNCTION__ (DeviceObject %p, *DeviceObject %p)\n",
|
DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
|
||||||
DeviceObject, DeviceObject ? *DeviceObject : NULL);
|
DeviceObject, DeviceObject ? *DeviceObject : NULL);
|
||||||
|
|
||||||
/* if *DeviceObject is not NULL, we must use it as a PDO, and don't create a new one */
|
/* Create the service name (eg. ACPI_HAL) */
|
||||||
|
ServiceName.Buffer = DriverObject->DriverName.Buffer +
|
||||||
|
sizeof(DRIVER_ROOT_NAME) / sizeof(WCHAR) - 1;
|
||||||
|
ServiceName.Length = DriverObject->DriverName.Length -
|
||||||
|
sizeof(DRIVER_ROOT_NAME) + sizeof(WCHAR);
|
||||||
|
ServiceName.MaximumLength = ServiceName.Length;
|
||||||
|
|
||||||
|
/* If the interface type is unknown, treat it as internal */
|
||||||
|
if (LegacyBusType == InterfaceTypeUndefined)
|
||||||
|
LegacyBusType = Internal;
|
||||||
|
|
||||||
|
/* Get the string equivalent of the interface type */
|
||||||
|
IfString = IopGetInterfaceTypeString(LegacyBusType);
|
||||||
|
|
||||||
|
/* If NULL is returned then it's a bad type */
|
||||||
|
if (!IfString)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
/* We use the caller's PDO if they supplied one */
|
||||||
if (DeviceObject && *DeviceObject)
|
if (DeviceObject && *DeviceObject)
|
||||||
{
|
{
|
||||||
Pdo = *DeviceObject;
|
Pdo = *DeviceObject;
|
||||||
|
DeviceNode = IopGetDeviceNode(*DeviceObject);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UNICODE_STRING ServiceName;
|
/* Create the PDO */
|
||||||
ServiceName.Buffer = DriverObject->DriverName.Buffer +
|
Status = PnpRootCreateDevice(&ServiceName,
|
||||||
sizeof(DRIVER_ROOT_NAME) / sizeof(WCHAR) - 1;
|
&Pdo);
|
||||||
ServiceName.Length = DriverObject->DriverName.Length -
|
if (!NT_SUCCESS(Status))
|
||||||
sizeof(DRIVER_ROOT_NAME) + sizeof(WCHAR);
|
{
|
||||||
ServiceName.MaximumLength = ServiceName.Length;
|
DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
/* create a new PDO and return it in *DeviceObject */
|
/* Create the device node for the new PDO */
|
||||||
Status = IopCreateDeviceNode(IopRootDeviceNode,
|
Status = IopCreateDeviceNode(IopRootDeviceNode,
|
||||||
|
Pdo,
|
||||||
NULL,
|
NULL,
|
||||||
&ServiceName,
|
|
||||||
&DeviceNode);
|
&DeviceNode);
|
||||||
|
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
|
@ -60,13 +171,129 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
|
||||||
DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
|
DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Pdo = DeviceNode->PhysicalDeviceObject;
|
/* We don't call AddDevice for devices reported this way */
|
||||||
if (DeviceObject) *DeviceObject = Pdo;
|
IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
|
||||||
}
|
|
||||||
|
|
||||||
/* we don't need to call AddDevice and send IRP_MN_START_DEVICE */
|
/* We don't send IRP_MN_START_DEVICE */
|
||||||
return Status;
|
IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
|
||||||
|
|
||||||
|
/* This is a legacy driver for this device */
|
||||||
|
IopDeviceNodeSetFlag(DeviceNode, DNF_LEGACY_DRIVER);
|
||||||
|
|
||||||
|
/* Perform a manual configuration of our device */
|
||||||
|
IopActionInterrogateDeviceStack(DeviceNode, DeviceNode->Parent);
|
||||||
|
IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
|
||||||
|
|
||||||
|
/* Open a handle to the instance path key */
|
||||||
|
Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, &InstanceKey);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
return Status;
|
||||||
|
|
||||||
|
/* Add DETECTEDInterfaceType\DriverName */
|
||||||
|
IdLength = 0;
|
||||||
|
IdLength += swprintf(&HardwareId[IdLength],
|
||||||
|
L"DETECTED%ls\\%wZ",
|
||||||
|
IfString,
|
||||||
|
&ServiceName);
|
||||||
|
HardwareId[IdLength++] = UNICODE_NULL;
|
||||||
|
|
||||||
|
/* Add DETECTED\DriverName */
|
||||||
|
IdLength += swprintf(&HardwareId[IdLength],
|
||||||
|
L"DETECTED\\%wZ",
|
||||||
|
&ServiceName);
|
||||||
|
HardwareId[IdLength++] = UNICODE_NULL;
|
||||||
|
|
||||||
|
/* Terminate the string with another null */
|
||||||
|
HardwareId[IdLength] = UNICODE_NULL;
|
||||||
|
|
||||||
|
/* Store the value for CompatibleIDs */
|
||||||
|
RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
|
||||||
|
Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT("Failed to write the compatible IDs: 0x%x\n", Status);
|
||||||
|
ZwClose(InstanceKey);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a hardware ID if the driver didn't report one */
|
||||||
|
RtlInitUnicodeString(&ValueName, L"HardwareID");
|
||||||
|
if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
|
||||||
|
{
|
||||||
|
/* Just use our most specific compatible ID */
|
||||||
|
IdLength = 0;
|
||||||
|
IdLength += swprintf(&HardwareId[IdLength],
|
||||||
|
L"DETECTED%ls\\%wZ",
|
||||||
|
IfString,
|
||||||
|
&ServiceName);
|
||||||
|
HardwareId[++IdLength] = UNICODE_NULL;
|
||||||
|
|
||||||
|
/* Write the value to the registry */
|
||||||
|
Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, HardwareId, IdLength * sizeof(WCHAR));
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT("Failed to write the hardware ID: 0x%x\n", Status);
|
||||||
|
ZwClose(InstanceKey);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assign the resources to the device node */
|
||||||
|
DeviceNode->BootResources = ResourceList;
|
||||||
|
DeviceNode->ResourceRequirements = ResourceRequirements;
|
||||||
|
|
||||||
|
/* Set appropriate flags */
|
||||||
|
if (DeviceNode->BootResources)
|
||||||
|
IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
|
||||||
|
|
||||||
|
if (DeviceNode->ResourceRequirements)
|
||||||
|
IopDeviceNodeSetFlag(DeviceNode, DNF_RESOURCE_REPORTED);
|
||||||
|
else
|
||||||
|
IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED);
|
||||||
|
|
||||||
|
/* Write the resource information to the registry */
|
||||||
|
IopSetDeviceInstanceData(InstanceKey, DeviceNode);
|
||||||
|
|
||||||
|
/* Close the instance key handle */
|
||||||
|
ZwClose(InstanceKey);
|
||||||
|
|
||||||
|
/* If the caller didn't get the resources assigned for us, do it now */
|
||||||
|
if (!ResourceAssigned)
|
||||||
|
{
|
||||||
|
IopDeviceNodeSetFlag(DeviceNode, DNF_ASSIGNING_RESOURCES);
|
||||||
|
Status = IopAssignDeviceResources(DeviceNode, &RequiredLength);
|
||||||
|
if (NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
Status = IopTranslateDeviceResources(DeviceNode, RequiredLength);
|
||||||
|
if (NT_SUCCESS(Status))
|
||||||
|
IopDeviceNodeSetFlag(DeviceNode, DNF_RESOURCE_ASSIGNED);
|
||||||
|
}
|
||||||
|
IopDeviceNodeClearFlag(DeviceNode, DNF_ASSIGNING_RESOURCES);
|
||||||
|
|
||||||
|
/* See if we failed */
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT("Assigning resources failed: 0x%x\n", Status);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Report the device's enumeration to umpnpmgr */
|
||||||
|
IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
|
||||||
|
&DeviceNode->InstancePath);
|
||||||
|
|
||||||
|
/* Report the device's arrival to umpnpmgr */
|
||||||
|
IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
|
||||||
|
&DeviceNode->InstancePath);
|
||||||
|
|
||||||
|
DPRINT1("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath);
|
||||||
|
|
||||||
|
/* Return the PDO */
|
||||||
|
if (DeviceObject) *DeviceObject = Pdo;
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -134,7 +134,7 @@ PnpRootCreateDevice(
|
||||||
IN PDEVICE_OBJECT *PhysicalDeviceObject)
|
IN PDEVICE_OBJECT *PhysicalDeviceObject)
|
||||||
{
|
{
|
||||||
PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
|
PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
|
||||||
UNICODE_STRING UnknownServiceName = RTL_CONSTANT_STRING(L"UNKNOWN");
|
UNICODE_STRING UnknownServiceName = RTL_CONSTANT_STRING(L"LEGACY_UNKNOWN");
|
||||||
PUNICODE_STRING LocalServiceName;
|
PUNICODE_STRING LocalServiceName;
|
||||||
PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension;
|
PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension;
|
||||||
WCHAR DevicePath[MAX_PATH + 1];
|
WCHAR DevicePath[MAX_PATH + 1];
|
||||||
|
@ -154,7 +154,7 @@ PnpRootCreateDevice(
|
||||||
DPRINT("Creating a PnP root device for service '%wZ'\n", LocalServiceName);
|
DPRINT("Creating a PnP root device for service '%wZ'\n", LocalServiceName);
|
||||||
|
|
||||||
/* Search for a free instance ID */
|
/* Search for a free instance ID */
|
||||||
_snwprintf(DevicePath, sizeof(DevicePath) / sizeof(WCHAR), L"%s\\LEGACY_%wZ", REGSTR_KEY_ROOTENUM, LocalServiceName);
|
_snwprintf(DevicePath, sizeof(DevicePath) / sizeof(WCHAR), L"%s\\%wZ", REGSTR_KEY_ROOTENUM, LocalServiceName);
|
||||||
for (i = 0; i < 9999; i++)
|
for (i = 0; i < 9999; i++)
|
||||||
{
|
{
|
||||||
_snwprintf(InstancePath, sizeof(InstancePath) / sizeof(WCHAR), L"%04lu", i);
|
_snwprintf(InstancePath, sizeof(InstancePath) / sizeof(WCHAR), L"%04lu", i);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue