2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/backpln/portcls/api.cpp
5 * PURPOSE: Port api functions
6 * PROGRAMMER: Johannes Anderwald
12 KSPIN_INTERFACE PinInterfaces
[] =
15 {STATIC_KSINTERFACESETID_Standard
},
16 KSINTERFACE_STANDARD_STREAMING
,
20 {STATIC_KSINTERFACESETID_Standard
},
21 KSINTERFACE_STANDARD_LOOPED_STREAMING
,
29 KsoDispatchCreateWithGenericFactory(
34 return STATUS_NOT_IMPLEMENTED
;
39 KsoGetIrpTargetFromFileObject(
40 PFILE_OBJECT FileObject
)
42 PC_ASSERT(FileObject
);
44 // IrpTarget is stored in FsContext
45 return (IIrpTarget
*)FileObject
->FsContext
;
50 KsoGetIrpTargetFromIrp(
53 PIO_STACK_LOCATION IoStack
;
55 // get current irp stack location
56 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
58 // IIrpTarget is stored in Context member
59 return (IIrpTarget
*)IoStack
->FileObject
->FsContext
;
64 PcHandleEnableEventWithTable(
66 IN PSUBDEVICE_DESCRIPTOR Descriptor
)
69 KSEVENT_ITEM_IRP_STORAGE(Irp
) = (PKSEVENT_ITEM
)Descriptor
;
72 return KsEnableEvent(Irp
, Descriptor
->EventSetCount
, Descriptor
->EventSet
, NULL
, KSEVENTS_NONE
, NULL
);
77 PcHandleDisableEventWithTable(
79 IN PSUBDEVICE_DESCRIPTOR Descriptor
)
82 KSEVENT_ITEM_IRP_STORAGE(Irp
) = (PKSEVENT_ITEM
)Descriptor
;
86 return KsDisableEvent(Irp
, Descriptor
->EventList
, KSEVENTS_SPINLOCK
, (PVOID
)Descriptor
->EventListLock
);
92 PcHandlePropertyWithTable(
94 IN ULONG PropertySetCount
,
95 IN PKSPROPERTY_SET PropertySet
,
96 IN PSUBDEVICE_DESCRIPTOR SubDeviceDescriptor
)
99 PIO_STACK_LOCATION IoStack
;
101 PPCNODE_DESCRIPTOR Node
;
102 PPCPROPERTY_ITEM PropertyItem
;
106 PPCPROPERTY_REQUEST PropertyRequest
;
108 KSPROPERTY_ITEM_IRP_STORAGE(Irp
) = (PKSPROPERTY_ITEM
)SubDeviceDescriptor
;
110 /* try first KsPropertyHandler */
111 Status
= KsPropertyHandler(Irp
, PropertySetCount
, PropertySet
);
113 if (Status
!= STATUS_NOT_FOUND
)
116 // get current irp stack location
117 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
120 Property
= (PKSP_NODE
)IoStack
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
122 // check if this a GUID_NULL request
123 if (Status
== STATUS_NOT_FOUND
)
125 if (IoStack
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(KSP_NODE
) || !(Property
->Property
.Flags
& KSPROPERTY_TYPE_TOPOLOGY
))
128 // check if its a request for a topology node
129 if (IsEqualGUIDAligned(Property
->Property
.Set
, GUID_NULL
) && Property
->Property
.Id
== 0 && Property
->Property
.Flags
== (KSPROPERTY_TYPE_SETSUPPORT
| KSPROPERTY_TYPE_TOPOLOGY
))
131 if (Property
->NodeId
>= SubDeviceDescriptor
->DeviceDescriptor
->NodeCount
)
133 // request is out of bounds
134 Irp
->IoStatus
.Information
= 0;
135 return STATUS_INVALID_PARAMETER
;
138 Node
= (PPCNODE_DESCRIPTOR
)((ULONG_PTR
)SubDeviceDescriptor
->DeviceDescriptor
->Nodes
+ (Property
->NodeId
* SubDeviceDescriptor
->DeviceDescriptor
->NodeSize
));
140 if (!Node
->AutomationTable
)
142 // request is out of bounds
143 Irp
->IoStatus
.Information
= 0;
144 return STATUS_INVALID_PARAMETER
;
147 PC_ASSERT(Node
->AutomationTable
);
148 PC_ASSERT(Node
->AutomationTable
->PropertyCount
);
149 PC_ASSERT(Node
->AutomationTable
->PropertyItemSize
);
151 Buffer
= (LPGUID
)AllocateItem(NonPagedPool
, sizeof (GUID
) * Node
->AutomationTable
->PropertyCount
, TAG_PORTCLASS
);
153 return STATUS_INSUFFICIENT_RESOURCES
;
156 ULONG Count
= 0, SubIndex
;
157 PropertyItem
= (PCPROPERTY_ITEM
*)Node
->AutomationTable
->Properties
;
158 for (Index
= 0; Index
< Node
->AutomationTable
->PropertyCount
; Index
++)
161 for (SubIndex
= 0; SubIndex
< Count
; Index
++)
163 if (IsEqualGUIDAligned(Buffer
[SubIndex
], *PropertyItem
->Set
))
171 RtlMoveMemory(&Buffer
[Count
], PropertyItem
->Set
, sizeof (GUID
));
174 PropertyItem
= (PPCPROPERTY_ITEM
)((ULONG_PTR
)PropertyItem
+ Node
->AutomationTable
->PropertyItemSize
);
177 Irp
->IoStatus
.Information
= sizeof (GUID
) * Count
;
178 if (IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof (GUID
) * Count
)
181 FreeItem(Buffer
, TAG_PORTCLASS
);
182 return STATUS_MORE_ENTRIES
;
185 RtlMoveMemory(Irp
->UserBuffer
, Buffer
, sizeof (GUID
) * Count
);
186 FreeItem(Buffer
, TAG_PORTCLASS
);
187 return STATUS_SUCCESS
;
189 else /*if (Property->Property.Flags == (KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY) ||
190 Property->Property.Flags == (KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY) ||
191 Property->Property.Flags == (KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY)) */
193 //UNICODE_STRING GuidString;
195 if (Property
->NodeId
>= SubDeviceDescriptor
->DeviceDescriptor
->NodeCount
)
197 // request is out of bounds
198 Irp
->IoStatus
.Information
= 0;
199 return STATUS_INVALID_PARAMETER
;
202 Node
= (PPCNODE_DESCRIPTOR
)((ULONG_PTR
)SubDeviceDescriptor
->DeviceDescriptor
->Nodes
+ (Property
->NodeId
* SubDeviceDescriptor
->DeviceDescriptor
->NodeSize
));
204 if (!Node
->AutomationTable
)
206 // request is out of bounds
207 Irp
->IoStatus
.Information
= 0;
208 return STATUS_NOT_FOUND
;
211 PC_ASSERT(Node
->AutomationTable
);
212 PC_ASSERT(Node
->AutomationTable
->PropertyCount
);
213 PC_ASSERT(Node
->AutomationTable
->PropertyItemSize
);
215 PropertyItem
= (PCPROPERTY_ITEM
*)Node
->AutomationTable
->Properties
;
217 for(Index
= 0; Index
< Node
->AutomationTable
->PropertyCount
; Index
++)
219 if (IsEqualGUIDAligned(*PropertyItem
->Set
, Property
->Property
.Set
) && PropertyItem
->Id
== Property
->Property
.Id
)
221 if (Property
->Property
.Flags
& KSPROPERTY_TYPE_BASICSUPPORT
)
223 if (!(PropertyItem
->Flags
& KSPROPERTY_TYPE_BASICSUPPORT
))
225 PC_ASSERT(IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
>= sizeof(ULONG
));
226 PULONG Flags
= (PULONG
)Irp
->UserBuffer
;
231 if (PropertyItem
->Flags
& KSPROPERTY_TYPE_SET
)
232 *Flags
|= KSPROPERTY_TYPE_SET
;
234 if (PropertyItem
->Flags
& KSPROPERTY_TYPE_GET
)
235 *Flags
|= KSPROPERTY_TYPE_GET
;
237 Irp
->IoStatus
.Information
= sizeof(ULONG
);
239 if (IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
>= sizeof(KSPROPERTY_DESCRIPTION
))
241 /* get output buffer */
242 PKSPROPERTY_DESCRIPTION Description
= (PKSPROPERTY_DESCRIPTION
)Irp
->UserBuffer
;
245 Description
->DescriptionSize
= sizeof(KSPROPERTY_DESCRIPTION
);
246 Description
->PropTypeSet
.Set
= KSPROPTYPESETID_General
;
247 Description
->PropTypeSet
.Id
= 0;
248 Description
->PropTypeSet
.Flags
= 0;
249 Description
->MembersListCount
= 0;
250 Description
->Reserved
= 0;
252 Irp
->IoStatus
.Information
= sizeof(KSPROPERTY_DESCRIPTION
);
254 return STATUS_SUCCESS
;
259 PropertyRequest
= (PPCPROPERTY_REQUEST
)AllocateItem(NonPagedPool
, sizeof(PCPROPERTY_REQUEST
), TAG_PORTCLASS
);
260 if (!PropertyRequest
)
261 return STATUS_INSUFFICIENT_RESOURCES
;
263 PC_ASSERT(SubDeviceDescriptor
->UnknownMiniport
);
264 PropertyRequest
->MajorTarget
= SubDeviceDescriptor
->UnknownMiniport
;
265 PropertyRequest
->MinorTarget
= SubDeviceDescriptor
->UnknownStream
;
266 PropertyRequest
->Irp
= Irp
;
267 PropertyRequest
->Node
= Property
->NodeId
;
268 PropertyRequest
->PropertyItem
= PropertyItem
;
269 PropertyRequest
->Verb
= Property
->Property
.Flags
;
270 PropertyRequest
->InstanceSize
= IoStack
->Parameters
.DeviceIoControl
.InputBufferLength
- sizeof(KSNODEPROPERTY
);
271 PropertyRequest
->Instance
= (PVOID
)((ULONG_PTR
)IoStack
->Parameters
.DeviceIoControl
.Type3InputBuffer
+ sizeof(KSNODEPROPERTY
));
272 PropertyRequest
->ValueSize
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
273 PropertyRequest
->Value
= Irp
->UserBuffer
;
275 Status
= PropertyItem
->Handler(PropertyRequest
);
277 if (Status
!= STATUS_PENDING
)
279 //DPRINT("Status %x ValueSize %u
281 Irp
->IoStatus
.Information
= PropertyRequest
->ValueSize
;
282 ExFreePool(PropertyRequest
);
285 RtlStringFromGUID(Property
->Property
.Set
, &GuidString
);
286 DPRINT("Id %u Flags %x Set %S FlagsItem %x Status %x\n", Property
->Property
.Id
, Property
->Property
.Flags
, GuidString
.Buffer
, PropertyItem
->Flags
, Status
);
287 RtlFreeUnicodeString(&GuidString
);
291 PropertyItem
= (PPCPROPERTY_ITEM
)((ULONG_PTR
)PropertyItem
+ Node
->AutomationTable
->PropertyItemSize
);
294 RtlStringFromGUID(Property
->Property
.Set
, &GuidString
);
295 DPRINT("Id %u Flags %x Set %S Status %x\n", Property
->Property
.Id
, Property
->Property
.Flags
, GuidString
.Buffer
, Status
);
296 RtlFreeUnicodeString(&GuidString
);
305 PcAcquireFormatResources(
326 return STATUS_NOT_IMPLEMENTED
;
330 PcAddToPropertyTable(
338 return STATUS_NOT_IMPLEMENTED
;
349 return STATUS_NOT_IMPLEMENTED
;
353 DumpFilterDescriptor(
354 IN PPCFILTER_DESCRIPTOR FilterDescription
)
356 ULONG Index
, SubIndex
;
357 PPCPROPERTY_ITEM PropertyItem
;
358 PPCEVENT_ITEM EventItem
;
359 PPCNODE_DESCRIPTOR NodeDescriptor
;
360 UNICODE_STRING GuidString
;
364 DPRINT1("======================\n");
365 DPRINT1("Descriptor Automation Table%p\n",FilterDescription
->AutomationTable
);
367 if (FilterDescription
->AutomationTable
)
369 DPRINT1("FilterPropertiesCount %u FilterPropertySize %u Expected %u Events %u EventItemSize %u expected %u\n", FilterDescription
->AutomationTable
->PropertyCount
, FilterDescription
->AutomationTable
->PropertyItemSize
, sizeof(PCPROPERTY_ITEM
),
370 FilterDescription
->AutomationTable
->EventCount
, FilterDescription
->AutomationTable
->EventItemSize
, sizeof(PCEVENT_ITEM
));
371 if (FilterDescription
->AutomationTable
->PropertyCount
)
373 PropertyItem
= (PPCPROPERTY_ITEM
)FilterDescription
->AutomationTable
->Properties
;
375 for(Index
= 0; Index
< FilterDescription
->AutomationTable
->PropertyCount
; Index
++)
377 RtlStringFromGUID(*PropertyItem
->Set
, &GuidString
);
378 DPRINT("Property Index %u GUID %S Id %u Flags %x\n", Index
, GuidString
.Buffer
, PropertyItem
->Id
, PropertyItem
->Flags
);
380 PropertyItem
= (PPCPROPERTY_ITEM
)((ULONG_PTR
)PropertyItem
+ FilterDescription
->AutomationTable
->PropertyItemSize
);
383 EventItem
= (PPCEVENT_ITEM
)FilterDescription
->AutomationTable
->Events
;
384 for(Index
= 0; Index
< FilterDescription
->AutomationTable
->EventCount
; Index
++)
386 RtlStringFromGUID(*EventItem
->Set
, &GuidString
);
387 DPRINT1("EventIndex %u GUID %S Id %u Flags %x\n", Index
, GuidString
.Buffer
, EventItem
->Id
, EventItem
->Flags
);
389 EventItem
= (PPCEVENT_ITEM
)((ULONG_PTR
)EventItem
+ FilterDescription
->AutomationTable
->EventItemSize
);
395 if (FilterDescription
->Nodes
)
397 DPRINT1("NodeCount %u NodeSize %u expected %u\n", FilterDescription
->NodeCount
, FilterDescription
->NodeSize
, sizeof(PCNODE_DESCRIPTOR
));
398 NodeDescriptor
= (PPCNODE_DESCRIPTOR
)FilterDescription
->Nodes
;
399 for(Index
= 0; Index
< FilterDescription
->NodeCount
; Index
++)
401 DPRINT("Index %u AutomationTable %p\n", Index
, NodeDescriptor
->AutomationTable
);
403 if (NodeDescriptor
->AutomationTable
)
405 DPRINT1("Index %u EventCount %u\n", Index
, NodeDescriptor
->AutomationTable
->EventCount
);
406 EventItem
= (PPCEVENT_ITEM
)NodeDescriptor
->AutomationTable
->Events
;
407 for(SubIndex
= 0; SubIndex
< NodeDescriptor
->AutomationTable
->EventCount
; SubIndex
++)
409 RtlStringFromGUID(*EventItem
->Set
, &GuidString
);
410 DPRINT1("EventIndex %u GUID %S Id %u Flags %x\n", Index
, GuidString
.Buffer
, EventItem
->Id
, EventItem
->Flags
);
412 EventItem
= (PPCEVENT_ITEM
)((ULONG_PTR
)EventItem
+ FilterDescription
->AutomationTable
->EventItemSize
);
418 NodeDescriptor
= (PPCNODE_DESCRIPTOR
)((ULONG_PTR
)NodeDescriptor
+ FilterDescription
->NodeSize
);
427 DPRINT1("======================\n");
432 PcCreateSubdeviceDescriptor(
433 OUT SUBDEVICE_DESCRIPTOR
** OutSubdeviceDescriptor
,
434 IN ULONG InterfaceCount
,
435 IN GUID
* InterfaceGuids
,
436 IN ULONG IdentifierCount
,
437 IN KSIDENTIFIER
*Identifier
,
438 IN ULONG FilterPropertiesCount
,
439 IN KSPROPERTY_SET
* FilterProperties
,
442 IN ULONG PinPropertiesCount
,
443 IN KSPROPERTY_SET
* PinProperties
,
444 IN ULONG EventSetCount
,
445 IN KSEVENT_SET
* EventSet
,
446 IN PPCFILTER_DESCRIPTOR FilterDescription
)
448 SUBDEVICE_DESCRIPTOR
* Descriptor
;
450 NTSTATUS Status
= STATUS_INSUFFICIENT_RESOURCES
;
451 PPCPIN_DESCRIPTOR SrcDescriptor
;
453 Descriptor
= (PSUBDEVICE_DESCRIPTOR
)AllocateItem(NonPagedPool
, sizeof(SUBDEVICE_DESCRIPTOR
), TAG_PORTCLASS
);
455 return STATUS_INSUFFICIENT_RESOURCES
;
457 // initialize physical / symbolic link connection list
458 InitializeListHead(&Descriptor
->SymbolicLinkList
);
459 InitializeListHead(&Descriptor
->PhysicalConnectionList
);
461 Descriptor
->Interfaces
= (GUID
*)AllocateItem(NonPagedPool
, sizeof(GUID
) * InterfaceCount
, TAG_PORTCLASS
);
462 if (!Descriptor
->Interfaces
)
465 // copy interface guids
466 RtlCopyMemory(Descriptor
->Interfaces
, InterfaceGuids
, sizeof(GUID
) * InterfaceCount
);
467 Descriptor
->InterfaceCount
= InterfaceCount
;
469 if (FilterPropertiesCount
)
472 /// handle driver properties
474 //DumpFilterDescriptor(FilterDescription);
476 Descriptor
->FilterPropertySet
= (PKSPROPERTY_SET
)AllocateItem(NonPagedPool
, sizeof(KSPROPERTY_SET
) * FilterPropertiesCount
, TAG_PORTCLASS
);
477 if (! Descriptor
->FilterPropertySet
)
480 Descriptor
->FilterPropertySetCount
= FilterPropertiesCount
;
481 for(Index
= 0; Index
< FilterPropertiesCount
; Index
++)
483 RtlMoveMemory(&Descriptor
->FilterPropertySet
[Index
], &FilterProperties
[Index
], sizeof(KSPROPERTY_SET
));
487 Descriptor
->Topology
= (PKSTOPOLOGY
)AllocateItem(NonPagedPool
, sizeof(KSTOPOLOGY
), TAG_PORTCLASS
);
488 if (!Descriptor
->Topology
)
491 if (FilterDescription
->ConnectionCount
)
493 Descriptor
->Topology
->TopologyConnections
= (PKSTOPOLOGY_CONNECTION
)AllocateItem(NonPagedPool
, sizeof(KSTOPOLOGY_CONNECTION
) * FilterDescription
->ConnectionCount
, TAG_PORTCLASS
);
494 if (!Descriptor
->Topology
->TopologyConnections
)
497 RtlMoveMemory((PVOID
)Descriptor
->Topology
->TopologyConnections
, FilterDescription
->Connections
, FilterDescription
->ConnectionCount
* sizeof(PCCONNECTION_DESCRIPTOR
));
498 Descriptor
->Topology
->TopologyConnectionsCount
= FilterDescription
->ConnectionCount
;
501 if (FilterDescription
->NodeCount
)
503 Descriptor
->Topology
->TopologyNodes
= (const GUID
*)AllocateItem(NonPagedPool
, sizeof(GUID
) * FilterDescription
->NodeCount
, TAG_PORTCLASS
);
504 if (!Descriptor
->Topology
->TopologyNodes
)
507 Descriptor
->Topology
->TopologyNodesNames
= (const GUID
*)AllocateItem(NonPagedPool
, sizeof(GUID
) * FilterDescription
->NodeCount
, TAG_PORTCLASS
);
508 if (!Descriptor
->Topology
->TopologyNodesNames
)
511 for(Index
= 0; Index
< FilterDescription
->NodeCount
; Index
++)
513 if (FilterDescription
->Nodes
[Index
].Type
)
515 RtlMoveMemory((PVOID
)&Descriptor
->Topology
->TopologyNodes
[Index
], FilterDescription
->Nodes
[Index
].Type
, sizeof(GUID
));
517 if (FilterDescription
->Nodes
[Index
].Name
)
519 RtlMoveMemory((PVOID
)&Descriptor
->Topology
->TopologyNodesNames
[Index
], FilterDescription
->Nodes
[Index
].Name
, sizeof(GUID
));
522 Descriptor
->Topology
->TopologyNodesCount
= FilterDescription
->NodeCount
;
525 if (FilterDescription
->PinCount
)
527 Descriptor
->Factory
.KsPinDescriptor
= (PKSPIN_DESCRIPTOR
)AllocateItem(NonPagedPool
, sizeof(KSPIN_DESCRIPTOR
) * FilterDescription
->PinCount
, TAG_PORTCLASS
);
528 if (!Descriptor
->Factory
.KsPinDescriptor
)
531 Descriptor
->Factory
.Instances
= (PPIN_INSTANCE_INFO
)AllocateItem(NonPagedPool
, FilterDescription
->PinCount
* sizeof(PIN_INSTANCE_INFO
), TAG_PORTCLASS
);
532 if (!Descriptor
->Factory
.Instances
)
535 Descriptor
->Factory
.PinDescriptorCount
= FilterDescription
->PinCount
;
536 Descriptor
->Factory
.PinDescriptorSize
= sizeof(KSPIN_DESCRIPTOR
);
538 SrcDescriptor
= (PPCPIN_DESCRIPTOR
)FilterDescription
->Pins
;
539 DPRINT("Size %u Expected %u Ex Size %u\n", FilterDescription
->PinSize
, sizeof(KSPIN_DESCRIPTOR
), sizeof(KSPIN_DESCRIPTOR_EX
));
541 // copy pin factories
542 for(Index
= 0; Index
< FilterDescription
->PinCount
; Index
++)
544 RtlMoveMemory(&Descriptor
->Factory
.KsPinDescriptor
[Index
], &SrcDescriptor
->KsPinDescriptor
, sizeof(KSPIN_DESCRIPTOR
));
546 Descriptor
->Factory
.KsPinDescriptor
[Index
].Interfaces
= PinInterfaces
;
547 Descriptor
->Factory
.KsPinDescriptor
[Index
].InterfacesCount
= sizeof(PinInterfaces
) / sizeof(KSPIN_INTERFACE
);
549 DPRINT("Index %u DataRangeCount %u\n", Index
, SrcDescriptor
->KsPinDescriptor
.DataRangesCount
);
551 Descriptor
->Factory
.Instances
[Index
].CurrentPinInstanceCount
= 0;
552 Descriptor
->Factory
.Instances
[Index
].MaxFilterInstanceCount
= SrcDescriptor
->MaxFilterInstanceCount
;
553 Descriptor
->Factory
.Instances
[Index
].MaxGlobalInstanceCount
= SrcDescriptor
->MaxGlobalInstanceCount
;
554 Descriptor
->Factory
.Instances
[Index
].MinFilterInstanceCount
= SrcDescriptor
->MinFilterInstanceCount
;
555 SrcDescriptor
= (PPCPIN_DESCRIPTOR
)((ULONG_PTR
)SrcDescriptor
+ FilterDescription
->PinSize
);
559 Descriptor
->DeviceDescriptor
= FilterDescription
;
560 *OutSubdeviceDescriptor
= Descriptor
;
561 return STATUS_SUCCESS
;
566 if (Descriptor
->Interfaces
)
567 FreeItem(Descriptor
->Interfaces
, TAG_PORTCLASS
);
569 if (Descriptor
->Factory
.KsPinDescriptor
)
570 FreeItem(Descriptor
->Factory
.KsPinDescriptor
, TAG_PORTCLASS
);
572 FreeItem(Descriptor
, TAG_PORTCLASS
);
579 PcValidateConnectRequest(
581 IN KSPIN_FACTORY
* Factory
,
582 OUT PKSPIN_CONNECT
* Connect
)
584 return KsValidateConnectRequest(Irp
, Factory
->PinDescriptorCount
, Factory
->KsPinDescriptor
, Connect
);