]> git.proxmox.com Git - mirror_edk2.git/blob - EdkNt32Pkg/Dxe/WinNtThunk/Bus/SerialIo/WinNtSerialIo.c
38c03a8bbc9285947e549c339375aed806b575cb
[mirror_edk2.git] / EdkNt32Pkg / Dxe / WinNtThunk / Bus / SerialIo / WinNtSerialIo.c
1 /*++
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 WinNtSerialIo.c
15
16 Abstract:
17
18 Our DriverBinding member functions operate on the handles
19 created by the NT Bus driver.
20
21 Handle(1) - WinNtIo - DevicePath(1)
22
23 If a serial port is added to the system this driver creates a new handle.
24 The new handle is required, since the serial device must add an UART device
25 pathnode.
26
27 Handle(2) - SerialIo - DevicePath(1)\UART
28
29 The driver then adds a gEfiWinNtSerialPortGuid as a protocol to Handle(1).
30 The instance data for this protocol is the private data used to create
31 Handle(2).
32
33 Handle(1) - WinNtIo - DevicePath(1) - WinNtSerialPort
34
35 If the driver is unloaded Handle(2) is removed from the system and
36 gEfiWinNtSerialPortGuid is removed from Handle(1).
37
38 Note: Handle(1) is any handle created by the Win NT Bus driver that is passed
39 into the DriverBinding member functions of this driver. This driver requires
40 a Handle(1) to contain a WinNtIo protocol, a DevicePath protocol, and
41 the TypeGuid in the WinNtIo must be gEfiWinNtSerialPortGuid.
42
43 If Handle(1) contains a gEfiWinNtSerialPortGuid protocol then the driver is
44 loaded on the device.
45
46 --*/
47
48 #include "WinNtSerialIo.h"
49
50 EFI_DRIVER_BINDING_PROTOCOL gWinNtSerialIoDriverBinding = {
51 WinNtSerialIoDriverBindingSupported,
52 WinNtSerialIoDriverBindingStart,
53 WinNtSerialIoDriverBindingStop,
54 0x10,
55 NULL,
56 NULL
57 };
58
59 STATIC
60 EFI_STATUS
61 EFIAPI
62 WinNtSerialIoDriverBindingSupported (
63 IN EFI_DRIVER_BINDING_PROTOCOL *This,
64 IN EFI_HANDLE Handle,
65 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
66 )
67 /*++
68
69 Routine Description:
70
71 Arguments:
72
73 Returns:
74
75 None
76
77 --*/
78 // TODO: This - add argument and description to function comment
79 // TODO: Handle - add argument and description to function comment
80 // TODO: RemainingDevicePath - add argument and description to function comment
81 // TODO: EFI_SUCCESS - add return value to function comment
82 // TODO: EFI_SUCCESS - add return value to function comment
83 {
84 EFI_STATUS Status;
85 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
86 EFI_WIN_NT_IO_PROTOCOL *WinNtIo;
87 UART_DEVICE_PATH *UartNode;
88
89 //
90 // Open the IO Abstraction(s) needed to perform the supported test
91 //
92 Status = gBS->OpenProtocol (
93 Handle,
94 &gEfiDevicePathProtocolGuid,
95 &ParentDevicePath,
96 This->DriverBindingHandle,
97 Handle,
98 EFI_OPEN_PROTOCOL_BY_DRIVER
99 );
100 if (Status == EFI_ALREADY_STARTED) {
101 return EFI_SUCCESS;
102 }
103
104 if (EFI_ERROR (Status)) {
105 return Status;
106 }
107
108 gBS->CloseProtocol (
109 Handle,
110 &gEfiDevicePathProtocolGuid,
111 This->DriverBindingHandle,
112 Handle
113 );
114
115 Status = gBS->OpenProtocol (
116 Handle,
117 &gEfiWinNtIoProtocolGuid,
118 &WinNtIo,
119 This->DriverBindingHandle,
120 Handle,
121 EFI_OPEN_PROTOCOL_BY_DRIVER
122 );
123 if (Status == EFI_ALREADY_STARTED) {
124 return EFI_SUCCESS;
125 }
126
127 if (EFI_ERROR (Status)) {
128 return Status;
129 }
130
131 //
132 // Make sure that the WinNt Thunk Protocol is valid
133 //
134 if (WinNtIo->WinNtThunk->Signature != EFI_WIN_NT_THUNK_PROTOCOL_SIGNATURE) {
135 Status = EFI_UNSUPPORTED;
136 goto Error;
137 }
138
139 //
140 // Check the GUID to see if this is a handle type the driver supports
141 //
142 if (!CompareGuid (WinNtIo->TypeGuid, &gEfiWinNtSerialPortGuid)) {
143 Status = EFI_UNSUPPORTED;
144 goto Error;
145 }
146
147 if (RemainingDevicePath != NULL) {
148 Status = EFI_UNSUPPORTED;
149 UartNode = (UART_DEVICE_PATH *) RemainingDevicePath;
150 if (UartNode->Header.Type != MESSAGING_DEVICE_PATH ||
151 UartNode->Header.SubType != MSG_UART_DP ||
152 DevicePathNodeLength((EFI_DEVICE_PATH_PROTOCOL *)UartNode) != sizeof(UART_DEVICE_PATH)) {
153 goto Error;
154 }
155 if (UartNode->BaudRate < 0 || UartNode->BaudRate > SERIAL_PORT_MAX_BAUD_RATE) {
156 goto Error;
157 }
158 if (UartNode->Parity < NoParity || UartNode->Parity > SpaceParity) {
159 goto Error;
160 }
161 if (UartNode->DataBits < 5 || UartNode->DataBits > 8) {
162 goto Error;
163 }
164 if (UartNode->StopBits < OneStopBit || UartNode->StopBits > TwoStopBits) {
165 goto Error;
166 }
167 if ((UartNode->DataBits == 5) && (UartNode->StopBits == TwoStopBits)) {
168 goto Error;
169 }
170 if ((UartNode->DataBits >= 6) && (UartNode->DataBits <= 8) && (UartNode->StopBits == OneFiveStopBits)) {
171 goto Error;
172 }
173 Status = EFI_SUCCESS;
174 }
175
176 Error:
177 //
178 // Close the I/O Abstraction(s) used to perform the supported test
179 //
180 gBS->CloseProtocol (
181 Handle,
182 &gEfiWinNtIoProtocolGuid,
183 This->DriverBindingHandle,
184 Handle
185 );
186
187 return Status;
188 }
189
190 STATIC
191 EFI_STATUS
192 EFIAPI
193 WinNtSerialIoDriverBindingStart (
194 IN EFI_DRIVER_BINDING_PROTOCOL *This,
195 IN EFI_HANDLE Handle,
196 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
197 )
198 /*++
199
200 Routine Description:
201
202 Arguments:
203
204 Returns:
205
206 None
207
208 --*/
209 // TODO: This - add argument and description to function comment
210 // TODO: Handle - add argument and description to function comment
211 // TODO: RemainingDevicePath - add argument and description to function comment
212 // TODO: EFI_SUCCESS - add return value to function comment
213 // TODO: EFI_SUCCESS - add return value to function comment
214 {
215 EFI_STATUS Status;
216 EFI_WIN_NT_IO_PROTOCOL *WinNtIo;
217 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
218 HANDLE NtHandle;
219 UART_DEVICE_PATH Node;
220 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
221 EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
222 UINTN EntryCount;
223 UINTN Index;
224 EFI_SERIAL_IO_PROTOCOL *SerialIo;
225
226 Private = NULL;
227 NtHandle = INVALID_HANDLE_VALUE;
228
229 //
230 // Grab the protocols we need
231 //
232 Status = gBS->OpenProtocol (
233 Handle,
234 &gEfiDevicePathProtocolGuid,
235 &ParentDevicePath,
236 This->DriverBindingHandle,
237 Handle,
238 EFI_OPEN_PROTOCOL_BY_DRIVER
239 );
240 if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
241 return Status;
242 }
243
244 //
245 // Grab the IO abstraction we need to get any work done
246 //
247 Status = gBS->OpenProtocol (
248 Handle,
249 &gEfiWinNtIoProtocolGuid,
250 &WinNtIo,
251 This->DriverBindingHandle,
252 Handle,
253 EFI_OPEN_PROTOCOL_BY_DRIVER
254 );
255 if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
256 gBS->CloseProtocol (
257 Handle,
258 &gEfiDevicePathProtocolGuid,
259 This->DriverBindingHandle,
260 Handle
261 );
262 return Status;
263 }
264
265 if (Status == EFI_ALREADY_STARTED) {
266
267 if (RemainingDevicePath == NULL) {
268 return EFI_SUCCESS;
269 }
270
271 //
272 // Make sure a child handle does not already exist. This driver can only
273 // produce one child per serial port.
274 //
275 Status = gBS->OpenProtocolInformation (
276 Handle,
277 &gEfiWinNtIoProtocolGuid,
278 &OpenInfoBuffer,
279 &EntryCount
280 );
281 if (EFI_ERROR (Status)) {
282 return Status;
283 }
284
285 Status = EFI_ALREADY_STARTED;
286 for (Index = 0; Index < EntryCount; Index++) {
287 if (OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
288 Status = gBS->OpenProtocol (
289 OpenInfoBuffer[Index].ControllerHandle,
290 &gEfiSerialIoProtocolGuid,
291 &SerialIo,
292 This->DriverBindingHandle,
293 Handle,
294 EFI_OPEN_PROTOCOL_GET_PROTOCOL
295 );
296 if (!EFI_ERROR (Status)) {
297 CopyMem (&Node, RemainingDevicePath, sizeof (UART_DEVICE_PATH));
298 Status = SerialIo->SetAttributes (
299 SerialIo,
300 Node.BaudRate,
301 SerialIo->Mode->ReceiveFifoDepth,
302 SerialIo->Mode->Timeout,
303 Node.Parity,
304 Node.DataBits,
305 Node.StopBits
306 );
307 }
308 break;
309 }
310 }
311
312 gBS->FreePool (OpenInfoBuffer);
313 return Status;
314 }
315
316 //
317 // Check to see if we can access the hardware device. If it's Open in NT we
318 // will not get access.
319 //
320 NtHandle = WinNtIo->WinNtThunk->CreateFile (
321 WinNtIo->EnvString,
322 GENERIC_READ | GENERIC_WRITE,
323 0,
324 NULL,
325 OPEN_EXISTING,
326 0,
327 NULL
328 );
329 if (NtHandle == INVALID_HANDLE_VALUE) {
330 Status = EFI_DEVICE_ERROR;
331 goto Error;
332 }
333
334 //
335 // Construct Private data
336 //
337 Status = gBS->AllocatePool (
338 EfiBootServicesData,
339 sizeof (WIN_NT_SERIAL_IO_PRIVATE_DATA),
340 &Private
341 );
342 if (EFI_ERROR (Status)) {
343 goto Error;
344 }
345
346 //
347 // This signature must be valid before any member function is called
348 //
349 Private->Signature = WIN_NT_SERIAL_IO_PRIVATE_DATA_SIGNATURE;
350 Private->NtHandle = NtHandle;
351 Private->ControllerHandle = Handle;
352 Private->Handle = NULL;
353 Private->WinNtThunk = WinNtIo->WinNtThunk;
354 Private->ParentDevicePath = ParentDevicePath;
355 Private->ControllerNameTable = NULL;
356
357 Private->SoftwareLoopbackEnable = FALSE;
358 Private->HardwareLoopbackEnable = FALSE;
359 Private->HardwareFlowControl = FALSE;
360 Private->Fifo.First = 0;
361 Private->Fifo.Last = 0;
362 Private->Fifo.Surplus = SERIAL_MAX_BUFFER_SIZE;
363
364 AddUnicodeString (
365 "eng",
366 gWinNtSerialIoComponentName.SupportedLanguages,
367 &Private->ControllerNameTable,
368 WinNtIo->EnvString
369 );
370
371 Private->SerialIo.Revision = SERIAL_IO_INTERFACE_REVISION;
372 Private->SerialIo.Reset = WinNtSerialIoReset;
373 Private->SerialIo.SetAttributes = WinNtSerialIoSetAttributes;
374 Private->SerialIo.SetControl = WinNtSerialIoSetControl;
375 Private->SerialIo.GetControl = WinNtSerialIoGetControl;
376 Private->SerialIo.Write = WinNtSerialIoWrite;
377 Private->SerialIo.Read = WinNtSerialIoRead;
378 Private->SerialIo.Mode = &Private->SerialIoMode;
379
380 if (RemainingDevicePath != NULL) {
381 //
382 // Match the configuration of the RemainingDevicePath. IsHandleSupported()
383 // already checked to make sure the RemainingDevicePath contains settings
384 // that we can support.
385 //
386 CopyMem (&Private->UartDevicePath, RemainingDevicePath, sizeof (UART_DEVICE_PATH));
387 } else {
388 //
389 // Build the device path by appending the UART node to the ParentDevicePath
390 // from the WinNtIo handle. The Uart setings are zero here, since
391 // SetAttribute() will update them to match the default setings.
392 //
393 ZeroMem (&Private->UartDevicePath, sizeof (UART_DEVICE_PATH));
394 Private->UartDevicePath.Header.Type = MESSAGING_DEVICE_PATH;
395 Private->UartDevicePath.Header.SubType = MSG_UART_DP;
396 SetDevicePathNodeLength ((EFI_DEVICE_PATH_PROTOCOL *) &Private->UartDevicePath, sizeof (UART_DEVICE_PATH));
397 }
398
399 //
400 // Build the device path by appending the UART node to the ParentDevicePath
401 // from the WinNtIo handle. The Uart setings are zero here, since
402 // SetAttribute() will update them to match the current setings.
403 //
404 Private->DevicePath = AppendDevicePathNode (
405 ParentDevicePath,
406 (EFI_DEVICE_PATH_PROTOCOL *) &Private->UartDevicePath
407 );
408 if (Private->DevicePath == NULL) {
409 Status = EFI_OUT_OF_RESOURCES;
410 goto Error;
411 }
412
413 //
414 // Fill in Serial I/O Mode structure based on either the RemainingDevicePath or defaults.
415 //
416 Private->SerialIoMode.ControlMask = SERIAL_CONTROL_MASK;
417 Private->SerialIoMode.Timeout = SERIAL_TIMEOUT_DEFAULT;
418 Private->SerialIoMode.BaudRate = Private->UartDevicePath.BaudRate;
419 Private->SerialIoMode.ReceiveFifoDepth = SERIAL_FIFO_DEFAULT;
420 Private->SerialIoMode.DataBits = Private->UartDevicePath.DataBits;
421 Private->SerialIoMode.Parity = Private->UartDevicePath.Parity;
422 Private->SerialIoMode.StopBits = Private->UartDevicePath.StopBits;
423
424 //
425 // Issue a reset to initialize the COM port
426 //
427 Status = Private->SerialIo.Reset (&Private->SerialIo);
428 if (EFI_ERROR (Status)) {
429 goto Error;
430 }
431
432 //
433 // Create new child handle
434 //
435 Status = gBS->InstallMultipleProtocolInterfaces (
436 &Private->Handle,
437 &gEfiSerialIoProtocolGuid,
438 &Private->SerialIo,
439 &gEfiDevicePathProtocolGuid,
440 Private->DevicePath,
441 NULL
442 );
443 if (EFI_ERROR (Status)) {
444 goto Error;
445 }
446
447 //
448 // Open For Child Device
449 //
450 Status = gBS->OpenProtocol (
451 Handle,
452 &gEfiWinNtIoProtocolGuid,
453 &WinNtIo,
454 This->DriverBindingHandle,
455 Private->Handle,
456 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
457 );
458 if (EFI_ERROR (Status)) {
459 goto Error;
460 }
461
462 return EFI_SUCCESS;
463
464 Error:
465 //
466 // Use the Stop() function to free all resources allocated in Start()
467 //
468 if (Private != NULL) {
469 if (Private->Handle != NULL) {
470 This->Stop (This, Handle, 1, &Private->Handle);
471 } else {
472 if (NtHandle != INVALID_HANDLE_VALUE) {
473 Private->WinNtThunk->CloseHandle (NtHandle);
474 }
475
476 if (Private->DevicePath != NULL) {
477 gBS->FreePool (Private->DevicePath);
478 }
479
480 FreeUnicodeStringTable (Private->ControllerNameTable);
481
482 gBS->FreePool (Private);
483 }
484 }
485
486 This->Stop (This, Handle, 0, NULL);
487
488 return Status;
489 }
490
491 STATIC
492 EFI_STATUS
493 EFIAPI
494 WinNtSerialIoDriverBindingStop (
495 IN EFI_DRIVER_BINDING_PROTOCOL *This,
496 IN EFI_HANDLE Handle,
497 IN UINTN NumberOfChildren,
498 IN EFI_HANDLE *ChildHandleBuffer
499 )
500 /*++
501
502 Routine Description:
503
504 TODO: Add function description
505
506 Arguments:
507
508 This - TODO: add argument description
509 Handle - TODO: add argument description
510 NumberOfChildren - TODO: add argument description
511 ChildHandleBuffer - TODO: add argument description
512
513 Returns:
514
515 EFI_DEVICE_ERROR - TODO: Add description for return value
516 EFI_SUCCESS - TODO: Add description for return value
517
518 --*/
519 {
520 EFI_STATUS Status;
521 UINTN Index;
522 BOOLEAN AllChildrenStopped;
523 EFI_SERIAL_IO_PROTOCOL *SerialIo;
524 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
525 EFI_WIN_NT_IO_PROTOCOL *WinNtIo;
526
527 //
528 // Complete all outstanding transactions to Controller.
529 // Don't allow any new transaction to Controller to be started.
530 //
531
532 if (NumberOfChildren == 0) {
533 //
534 // Close the bus driver
535 //
536 Status = gBS->CloseProtocol (
537 Handle,
538 &gEfiWinNtIoProtocolGuid,
539 This->DriverBindingHandle,
540 Handle
541 );
542 Status = gBS->CloseProtocol (
543 Handle,
544 &gEfiDevicePathProtocolGuid,
545 This->DriverBindingHandle,
546 Handle
547 );
548 return Status;
549 }
550
551 AllChildrenStopped = TRUE;
552
553 for (Index = 0; Index < NumberOfChildren; Index++) {
554 Status = gBS->OpenProtocol (
555 ChildHandleBuffer[Index],
556 &gEfiSerialIoProtocolGuid,
557 &SerialIo,
558 This->DriverBindingHandle,
559 Handle,
560 EFI_OPEN_PROTOCOL_GET_PROTOCOL
561 );
562 if (!EFI_ERROR (Status)) {
563 Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (SerialIo);
564
565 ASSERT (Private->Handle == ChildHandleBuffer[Index]);
566
567 Status = gBS->CloseProtocol (
568 Handle,
569 &gEfiWinNtIoProtocolGuid,
570 This->DriverBindingHandle,
571 ChildHandleBuffer[Index]
572 );
573
574 Status = gBS->UninstallMultipleProtocolInterfaces (
575 ChildHandleBuffer[Index],
576 &gEfiSerialIoProtocolGuid,
577 &Private->SerialIo,
578 &gEfiDevicePathProtocolGuid,
579 Private->DevicePath,
580 NULL
581 );
582
583 if (EFI_ERROR (Status)) {
584 gBS->OpenProtocol (
585 Handle,
586 &gEfiWinNtIoProtocolGuid,
587 (VOID **) &WinNtIo,
588 This->DriverBindingHandle,
589 ChildHandleBuffer[Index],
590 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
591 );
592 } else {
593 Private->WinNtThunk->CloseHandle (Private->NtHandle);
594
595 gBS->FreePool (Private->DevicePath);
596
597 FreeUnicodeStringTable (Private->ControllerNameTable);
598
599 gBS->FreePool (Private);
600 }
601 }
602
603 if (EFI_ERROR (Status)) {
604 AllChildrenStopped = FALSE;
605 }
606 }
607
608 if (!AllChildrenStopped) {
609 return EFI_DEVICE_ERROR;
610 }
611
612 return EFI_SUCCESS;
613 }
614
615 //
616 // Serial IO Protocol member functions
617 //
618
619 STATIC
620 EFI_STATUS
621 EFIAPI
622 WinNtSerialIoReset (
623 IN EFI_SERIAL_IO_PROTOCOL *This
624 )
625 /*++
626
627 Routine Description:
628
629 TODO: Add function description
630
631 Arguments:
632
633 This - TODO: add argument description
634
635 Returns:
636
637 TODO: add return values
638
639 --*/
640 {
641 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
642 EFI_TPL Tpl;
643
644 Tpl = gBS->RaiseTPL (EFI_TPL_NOTIFY);
645
646 Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This);
647
648 Private->WinNtThunk->PurgeComm (
649 Private->NtHandle,
650 PURGE_TXCLEAR | PURGE_RXCLEAR
651 );
652
653 gBS->RestoreTPL (Tpl);
654
655 return This->SetAttributes (
656 This,
657 This->Mode->BaudRate,
658 This->Mode->ReceiveFifoDepth,
659 This->Mode->Timeout,
660 This->Mode->Parity,
661 (UINT8) This->Mode->DataBits,
662 This->Mode->StopBits
663 );
664 }
665
666 STATIC
667 EFI_STATUS
668 EFIAPI
669 WinNtSerialIoSetAttributes (
670 IN EFI_SERIAL_IO_PROTOCOL *This,
671 IN UINT64 BaudRate,
672 IN UINT32 ReceiveFifoDepth,
673 IN UINT32 Timeout,
674 IN EFI_PARITY_TYPE Parity,
675 IN UINT8 DataBits,
676 IN EFI_STOP_BITS_TYPE StopBits
677 )
678 /*++
679
680 Routine Description:
681
682 This function is used to set the attributes.
683
684 Arguments:
685
686 This - A pointer to the EFI_SERIAL_IO_PROTOCOL structrue.
687 BaudRate - The Baud rate of the serial device.
688 ReceiveFifoDepth - The request depth of fifo on receive side.
689 Timeout - the request timeout for a single charact.
690 Parity - The type of parity used in serial device.
691 DataBits - Number of deata bits used in serial device.
692 StopBits - Number of stop bits used in serial device.
693
694 Returns:
695 Status code
696
697 None
698
699 --*/
700 // TODO: EFI_SUCCESS - add return value to function comment
701 // TODO: EFI_DEVICE_ERROR - add return value to function comment
702 // TODO: EFI_DEVICE_ERROR - add return value to function comment
703 // TODO: EFI_DEVICE_ERROR - add return value to function comment
704 // TODO: EFI_SUCCESS - add return value to function comment
705 // TODO: EFI_DEVICE_ERROR - add return value to function comment
706 // TODO: EFI_SUCCESS - add return value to function comment
707 {
708 EFI_STATUS Status;
709 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
710 COMMTIMEOUTS PortTimeOuts;
711 DWORD ConvertedTime;
712 BOOL Result;
713 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
714 EFI_TPL Tpl;
715
716 Tpl = gBS->RaiseTPL (EFI_TPL_NOTIFY);
717
718 Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This);
719
720 //
721 // Some of our arguments have defaults if a null value is passed in, and
722 // we must set the default values if a null argument is passed in.
723 //
724 if (BaudRate == 0) {
725 BaudRate = SERIAL_BAUD_DEFAULT;
726 }
727
728 if (ReceiveFifoDepth == 0) {
729 ReceiveFifoDepth = SERIAL_FIFO_DEFAULT;
730 }
731
732 if (Timeout == 0) {
733 Timeout = SERIAL_TIMEOUT_DEFAULT;
734 }
735
736 if (Parity == DefaultParity) {
737 Parity = NoParity;
738 }
739
740 if (DataBits == 0) {
741 DataBits = SERIAL_DATABITS_DEFAULT;
742 }
743
744 if (StopBits == DefaultStopBits) {
745 StopBits = OneStopBit;
746 }
747 //
748 // See if the new attributes already match the current attributes
749 //
750 if (Private->UartDevicePath.BaudRate == BaudRate &&
751 Private->UartDevicePath.DataBits == DataBits &&
752 Private->UartDevicePath.Parity == Parity &&
753 Private->UartDevicePath.StopBits == StopBits &&
754 Private->SerialIoMode.ReceiveFifoDepth == ReceiveFifoDepth &&
755 Private->SerialIoMode.Timeout == Timeout ) {
756 gBS->RestoreTPL(Tpl);
757 return EFI_SUCCESS;
758 }
759
760 //
761 // Get current values from NT
762 //
763 ZeroMem (&Private->NtDCB, sizeof (DCB));
764 Private->NtDCB.DCBlength = sizeof (DCB);
765
766 if (!Private->WinNtThunk->GetCommState (Private->NtHandle, &Private->NtDCB)) {
767 Private->NtError = Private->WinNtThunk->GetLastError ();
768 DEBUG ((EFI_D_ERROR, "SerialSetAttributes: GetCommState %d\n", Private->NtError));
769 gBS->RestoreTPL (Tpl);
770 return EFI_DEVICE_ERROR;
771 }
772
773 //
774 // Map EFI com setting to NT
775 //
776 Private->NtDCB.BaudRate = ConvertBaud2Nt (BaudRate);
777 Private->NtDCB.ByteSize = ConvertData2Nt (DataBits);
778 Private->NtDCB.Parity = ConvertParity2Nt (Parity);
779 Private->NtDCB.StopBits = ConvertStop2Nt (StopBits);
780
781 Private->NtDCB.fBinary = TRUE;
782 Private->NtDCB.fParity = Private->NtDCB.Parity == NOPARITY ? FALSE : TRUE;
783 Private->NtDCB.fOutxCtsFlow = FALSE;
784 Private->NtDCB.fOutxDsrFlow = FALSE;
785 Private->NtDCB.fDtrControl = DTR_CONTROL_ENABLE;
786 Private->NtDCB.fDsrSensitivity = FALSE;
787 Private->NtDCB.fOutX = FALSE;
788 Private->NtDCB.fInX = FALSE;
789 Private->NtDCB.fRtsControl = RTS_CONTROL_ENABLE;
790 Private->NtDCB.fNull = FALSE;
791
792 //
793 // Set new values
794 //
795 Result = Private->WinNtThunk->SetCommState (Private->NtHandle, &Private->NtDCB);
796 if (!Result) {
797 Private->NtError = Private->WinNtThunk->GetLastError ();
798 DEBUG ((EFI_D_ERROR, "SerialSetAttributes: SetCommState %d\n", Private->NtError));
799 gBS->RestoreTPL (Tpl);
800 return EFI_DEVICE_ERROR;
801 }
802
803 //
804 // Set com port read/write timeout values
805 //
806 ConvertedTime = ConvertTime2Nt (Timeout);
807 PortTimeOuts.ReadIntervalTimeout = MAXDWORD;
808 PortTimeOuts.ReadTotalTimeoutMultiplier = 0;
809 PortTimeOuts.ReadTotalTimeoutConstant = ConvertedTime;
810 PortTimeOuts.WriteTotalTimeoutMultiplier = ConvertedTime == 0 ? 1 : ConvertedTime;
811 PortTimeOuts.WriteTotalTimeoutConstant = 0;
812
813 if (!Private->WinNtThunk->SetCommTimeouts (Private->NtHandle, &PortTimeOuts)) {
814 Private->NtError = Private->WinNtThunk->GetLastError ();
815 DEBUG ((EFI_D_ERROR, "SerialSetAttributes: SetCommTimeouts %d\n", Private->NtError));
816 gBS->RestoreTPL (Tpl);
817 return EFI_DEVICE_ERROR;
818 }
819
820 //
821 // Update mode
822 //
823 Private->SerialIoMode.BaudRate = BaudRate;
824 Private->SerialIoMode.ReceiveFifoDepth = ReceiveFifoDepth;
825 Private->SerialIoMode.Timeout = Timeout;
826 Private->SerialIoMode.Parity = Parity;
827 Private->SerialIoMode.DataBits = DataBits;
828 Private->SerialIoMode.StopBits = StopBits;
829
830 //
831 // See if Device Path Node has actually changed
832 //
833 if (Private->UartDevicePath.BaudRate == BaudRate &&
834 Private->UartDevicePath.DataBits == DataBits &&
835 Private->UartDevicePath.Parity == Parity &&
836 Private->UartDevicePath.StopBits == StopBits ) {
837 gBS->RestoreTPL(Tpl);
838 return EFI_SUCCESS;
839 }
840
841 //
842 // Update the device path
843 //
844 Private->UartDevicePath.BaudRate = BaudRate;
845 Private->UartDevicePath.DataBits = DataBits;
846 Private->UartDevicePath.Parity = (UINT8) Parity;
847 Private->UartDevicePath.StopBits = (UINT8) StopBits;
848
849 NewDevicePath = AppendDevicePathNode (
850 Private->ParentDevicePath,
851 (EFI_DEVICE_PATH_PROTOCOL *) &Private->UartDevicePath
852 );
853 if (NewDevicePath == NULL) {
854 gBS->RestoreTPL (Tpl);
855 return EFI_DEVICE_ERROR;
856 }
857
858 if (Private->Handle != NULL) {
859 Status = gBS->ReinstallProtocolInterface (
860 Private->Handle,
861 &gEfiDevicePathProtocolGuid,
862 Private->DevicePath,
863 NewDevicePath
864 );
865 if (EFI_ERROR (Status)) {
866 gBS->RestoreTPL (Tpl);
867 return Status;
868 }
869 }
870
871 if (Private->DevicePath != NULL) {
872 gBS->FreePool (Private->DevicePath);
873 }
874
875 Private->DevicePath = NewDevicePath;
876
877 gBS->RestoreTPL (Tpl);
878
879 return EFI_SUCCESS;
880 }
881
882 STATIC
883 EFI_STATUS
884 EFIAPI
885 WinNtSerialIoSetControl (
886 IN EFI_SERIAL_IO_PROTOCOL *This,
887 IN UINT32 Control
888 )
889 /*++
890
891 Routine Description:
892
893 TODO: Add function description
894
895 Arguments:
896
897 This - TODO: add argument description
898 Control - TODO: add argument description
899
900 Returns:
901
902 EFI_DEVICE_ERROR - TODO: Add description for return value
903 EFI_DEVICE_ERROR - TODO: Add description for return value
904 EFI_SUCCESS - TODO: Add description for return value
905
906 --*/
907 {
908 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
909 BOOL Result;
910 DCB Dcb;
911 EFI_TPL Tpl;
912
913 Tpl = gBS->RaiseTPL (EFI_TPL_NOTIFY);
914
915 Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This);
916
917 Result = Private->WinNtThunk->GetCommState (Private->NtHandle, &Dcb);
918
919 if (!Result) {
920 Private->NtError = Private->WinNtThunk->GetLastError ();
921 DEBUG ((EFI_D_ERROR, "SerialSetControl: GetCommState %d\n", Private->NtError));
922 gBS->RestoreTPL (Tpl);
923 return EFI_DEVICE_ERROR;
924 }
925
926 Dcb.fRtsControl = RTS_CONTROL_DISABLE;
927 Dcb.fDtrControl = DTR_CONTROL_DISABLE;
928 Private->HardwareFlowControl = FALSE;
929 Private->SoftwareLoopbackEnable = FALSE;
930 Private->HardwareLoopbackEnable = FALSE;
931
932 if (Control & EFI_SERIAL_REQUEST_TO_SEND) {
933 Dcb.fRtsControl = RTS_CONTROL_ENABLE;
934 }
935
936 if (Control & EFI_SERIAL_DATA_TERMINAL_READY) {
937 Dcb.fDtrControl = DTR_CONTROL_ENABLE;
938 }
939
940 if (Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {
941 Private->HardwareFlowControl = TRUE;
942 }
943
944 if (Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) {
945 Private->SoftwareLoopbackEnable = TRUE;
946 }
947
948 if (Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) {
949 Private->HardwareLoopbackEnable = TRUE;
950 }
951
952 Result = Private->WinNtThunk->SetCommState (
953 Private->NtHandle,
954 &Dcb
955 );
956
957 if (!Result) {
958 Private->NtError = Private->WinNtThunk->GetLastError ();
959 DEBUG ((EFI_D_ERROR, "SerialSetControl: SetCommState %d\n", Private->NtError));
960 gBS->RestoreTPL (Tpl);
961 return EFI_DEVICE_ERROR;
962 }
963
964 gBS->RestoreTPL (Tpl);
965
966 return EFI_SUCCESS;
967 }
968
969 STATIC
970 EFI_STATUS
971 EFIAPI
972 WinNtSerialIoGetControl (
973 IN EFI_SERIAL_IO_PROTOCOL *This,
974 OUT UINT32 *Control
975 )
976 /*++
977
978 Routine Description:
979
980 TODO: Add function description
981
982 Arguments:
983
984 This - TODO: add argument description
985 Control - TODO: add argument description
986
987 Returns:
988
989 EFI_DEVICE_ERROR - TODO: Add description for return value
990 EFI_DEVICE_ERROR - TODO: Add description for return value
991 EFI_DEVICE_ERROR - TODO: Add description for return value
992 EFI_SUCCESS - TODO: Add description for return value
993
994 --*/
995 {
996 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
997 DWORD ModemStatus;
998 DWORD Errors;
999 UINT32 Bits;
1000 DCB Dcb;
1001 EFI_TPL Tpl;
1002
1003 Tpl = gBS->RaiseTPL (EFI_TPL_NOTIFY);
1004
1005 Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This);
1006
1007 //
1008 // Get modem status
1009 //
1010 if (!Private->WinNtThunk->GetCommModemStatus (Private->NtHandle, &ModemStatus)) {
1011 Private->NtError = Private->WinNtThunk->GetLastError ();
1012 gBS->RestoreTPL (Tpl);
1013 return EFI_DEVICE_ERROR;
1014 }
1015
1016 Bits = 0;
1017 if (ModemStatus & MS_CTS_ON) {
1018 Bits |= EFI_SERIAL_CLEAR_TO_SEND;
1019 }
1020
1021 if (ModemStatus & MS_DSR_ON) {
1022 Bits |= EFI_SERIAL_DATA_SET_READY;
1023 }
1024
1025 if (ModemStatus & MS_RING_ON) {
1026 Bits |= EFI_SERIAL_RING_INDICATE;
1027 }
1028
1029 if (ModemStatus & MS_RLSD_ON) {
1030 Bits |= EFI_SERIAL_CARRIER_DETECT;
1031 }
1032
1033 //
1034 // Get ctrl status
1035 //
1036 if (!Private->WinNtThunk->GetCommState (Private->NtHandle, &Dcb)) {
1037 Private->NtError = Private->WinNtThunk->GetLastError ();
1038 DEBUG ((EFI_D_ERROR, "SerialGetControl: GetCommState %d\n", Private->NtError));
1039 gBS->RestoreTPL (Tpl);
1040 return EFI_DEVICE_ERROR;
1041 }
1042
1043 if (Dcb.fDtrControl == DTR_CONTROL_ENABLE) {
1044 Bits |= EFI_SERIAL_DATA_TERMINAL_READY;
1045 }
1046
1047 if (Dcb.fRtsControl == RTS_CONTROL_ENABLE) {
1048 Bits |= EFI_SERIAL_REQUEST_TO_SEND;
1049 }
1050
1051 if (Private->HardwareFlowControl) {
1052 Bits |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
1053 }
1054
1055 if (Private->SoftwareLoopbackEnable) {
1056 Bits |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
1057 }
1058
1059 if (Private->HardwareLoopbackEnable) {
1060 Bits |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;
1061 }
1062
1063 //
1064 // Get input buffer status
1065 //
1066 if (!Private->WinNtThunk->ClearCommError (Private->NtHandle, &Errors, &Private->NtComStatus)) {
1067 Private->NtError = Private->WinNtThunk->GetLastError ();
1068 DEBUG ((EFI_D_ERROR, "SerialGetControl: ClearCommError %d\n", Private->NtError));
1069 gBS->RestoreTPL (Tpl);
1070 return EFI_DEVICE_ERROR;
1071 }
1072
1073 if (Private->NtComStatus.cbInQue == 0) {
1074 Bits |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
1075 }
1076
1077 *Control = Bits;
1078
1079 gBS->RestoreTPL (Tpl);
1080
1081 return EFI_SUCCESS;
1082 }
1083
1084 STATIC
1085 EFI_STATUS
1086 EFIAPI
1087 WinNtSerialIoWrite (
1088 IN EFI_SERIAL_IO_PROTOCOL *This,
1089 IN OUT UINTN *BufferSize,
1090 IN VOID *Buffer
1091 )
1092 /*++
1093
1094 Routine Description:
1095
1096 TODO: Add function description
1097
1098 Arguments:
1099
1100 This - TODO: add argument description
1101 BufferSize - TODO: add argument description
1102 Buffer - TODO: add argument description
1103
1104 Returns:
1105
1106 EFI_DEVICE_ERROR - TODO: Add description for return value
1107 EFI_SUCCESS - TODO: Add description for return value
1108
1109 --*/
1110 {
1111 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
1112 UINT8 *ByteBuffer;
1113 UINTN TotalBytesWritten;
1114 DWORD BytesToGo;
1115 DWORD BytesWritten;
1116 BOOL Result;
1117 UINT32 Index;
1118 UINT32 Control;
1119 EFI_TPL Tpl;
1120
1121 Tpl = gBS->RaiseTPL (EFI_TPL_NOTIFY);
1122
1123 Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This);
1124
1125 ByteBuffer = (UINT8 *) Buffer;
1126 TotalBytesWritten = 0;
1127
1128 if (Private->SoftwareLoopbackEnable || Private->HardwareLoopbackEnable) {
1129 for (Index = 0; Index < *BufferSize; Index++) {
1130 if (IsaSerialFifoAdd (&Private->Fifo, ByteBuffer[Index]) == EFI_SUCCESS) {
1131 TotalBytesWritten++;
1132 } else {
1133 break;
1134 }
1135 }
1136 } else {
1137 BytesToGo = (DWORD) (*BufferSize);
1138
1139 do {
1140 if (Private->HardwareFlowControl) {
1141 //
1142 // Send RTS
1143 //
1144 WinNtSerialIoGetControl (&Private->SerialIo, &Control);
1145 Control |= EFI_SERIAL_REQUEST_TO_SEND;
1146 WinNtSerialIoSetControl (&Private->SerialIo, Control);
1147 }
1148
1149 //
1150 // Do the write
1151 //
1152 Result = Private->WinNtThunk->WriteFile (
1153 Private->NtHandle,
1154 &ByteBuffer[TotalBytesWritten],
1155 BytesToGo,
1156 &BytesWritten,
1157 NULL
1158 );
1159
1160 if (Private->HardwareFlowControl) {
1161 //
1162 // Assert RTS
1163 //
1164 WinNtSerialIoGetControl (&Private->SerialIo, &Control);
1165 Control &= ~ (UINT32) EFI_SERIAL_REQUEST_TO_SEND;
1166 WinNtSerialIoSetControl (&Private->SerialIo, Control);
1167 }
1168
1169 TotalBytesWritten += BytesWritten;
1170 BytesToGo -= BytesWritten;
1171 if (!Result) {
1172 Private->NtError = Private->WinNtThunk->GetLastError ();
1173 DEBUG ((EFI_D_ERROR, "SerialWrite: FileWrite %d\n", Private->NtError));
1174 *BufferSize = TotalBytesWritten;
1175 gBS->RestoreTPL (Tpl);
1176 return EFI_DEVICE_ERROR;
1177 }
1178 } while (BytesToGo > 0);
1179 }
1180
1181 *BufferSize = TotalBytesWritten;
1182
1183 gBS->RestoreTPL (Tpl);
1184
1185 return EFI_SUCCESS;
1186 }
1187
1188 STATIC
1189 EFI_STATUS
1190 EFIAPI
1191 WinNtSerialIoRead (
1192 IN EFI_SERIAL_IO_PROTOCOL *This,
1193 IN OUT UINTN *BufferSize,
1194 OUT VOID *Buffer
1195 )
1196 /*++
1197
1198 Routine Description:
1199
1200 TODO: Add function description
1201
1202 Arguments:
1203
1204 This - TODO: add argument description
1205 BufferSize - TODO: add argument description
1206 Buffer - TODO: add argument description
1207
1208 Returns:
1209
1210 EFI_DEVICE_ERROR - TODO: Add description for return value
1211
1212 --*/
1213 {
1214 WIN_NT_SERIAL_IO_PRIVATE_DATA *Private;
1215 BOOL Result;
1216 DWORD BytesRead;
1217 EFI_STATUS Status;
1218 UINT32 Index;
1219 UINT8 Data;
1220 UINT32 Control;
1221 EFI_TPL Tpl;
1222
1223 Tpl = gBS->RaiseTPL (EFI_TPL_NOTIFY);
1224
1225 Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This);
1226
1227 //
1228 // Do the read
1229 //
1230 if (Private->SoftwareLoopbackEnable || Private->HardwareLoopbackEnable) {
1231 for (Index = 0, BytesRead = 0; Index < *BufferSize; Index++) {
1232 if (IsaSerialFifoRemove (&Private->Fifo, &Data) == EFI_SUCCESS) {
1233 ((UINT8 *) Buffer)[Index] = Data;
1234 BytesRead++;
1235 } else {
1236 break;
1237 }
1238 }
1239 } else {
1240 if (Private->HardwareFlowControl) {
1241 WinNtSerialIoGetControl (&Private->SerialIo, &Control);
1242 Control |= EFI_SERIAL_DATA_TERMINAL_READY;
1243 WinNtSerialIoSetControl (&Private->SerialIo, Control);
1244 }
1245
1246 Result = Private->WinNtThunk->ReadFile (
1247 Private->NtHandle,
1248 Buffer,
1249 (DWORD) *BufferSize,
1250 &BytesRead,
1251 NULL
1252 );
1253
1254 if (Private->HardwareFlowControl) {
1255 WinNtSerialIoGetControl (&Private->SerialIo, &Control);
1256 Control &= ~ (UINT32) EFI_SERIAL_DATA_TERMINAL_READY;
1257 WinNtSerialIoSetControl (&Private->SerialIo, Control);
1258 }
1259
1260 if (!Result) {
1261 Private->NtError = Private->WinNtThunk->GetLastError ();
1262 gBS->RestoreTPL (Tpl);
1263 return EFI_DEVICE_ERROR;
1264 }
1265 }
1266
1267 if (BytesRead != *BufferSize) {
1268 Status = EFI_TIMEOUT;
1269 } else {
1270 Status = EFI_SUCCESS;
1271 }
1272
1273 *BufferSize = (UINTN) BytesRead;
1274
1275 gBS->RestoreTPL (Tpl);
1276
1277 return Status;
1278 }
1279
1280 BOOLEAN
1281 IsaSerialFifoFull (
1282 IN SERIAL_DEV_FIFO *Fifo
1283 )
1284 /*++
1285
1286 Routine Description:
1287 Detect whether specific FIFO is full or not
1288
1289 Arguments:
1290 Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO
1291
1292 Returns:
1293 TRUE: the FIFO is full
1294 FALSE: the FIFO is not full
1295
1296 --*/
1297 {
1298 if (Fifo->Surplus == 0) {
1299 return TRUE;
1300 }
1301
1302 return FALSE;
1303 }
1304
1305 BOOLEAN
1306 IsaSerialFifoEmpty (
1307 IN SERIAL_DEV_FIFO *Fifo
1308 )
1309 /*++
1310
1311 Routine Description:
1312 Detect whether specific FIFO is empty or not
1313
1314 Arguments:
1315 Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO
1316
1317 Returns:
1318 TRUE: the FIFO is empty
1319 FALSE: the FIFO is not empty
1320
1321 --*/
1322 {
1323 if (Fifo->Surplus == SERIAL_MAX_BUFFER_SIZE) {
1324 return TRUE;
1325 }
1326
1327 return FALSE;
1328 }
1329
1330 EFI_STATUS
1331 IsaSerialFifoAdd (
1332 IN SERIAL_DEV_FIFO *Fifo,
1333 IN UINT8 Data
1334 )
1335 /*++
1336
1337 Routine Description:
1338 Add data to specific FIFO
1339
1340 Arguments:
1341 Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO
1342 Data UINT8: the data added to FIFO
1343
1344 Returns:
1345 EFI_SUCCESS: Add data to specific FIFO successfully
1346 EFI_OUT_RESOURCE: Failed to add data because FIFO is already full
1347
1348 --*/
1349 // TODO: EFI_OUT_OF_RESOURCES - add return value to function comment
1350 {
1351 //
1352 // if FIFO full can not add data
1353 //
1354 if (IsaSerialFifoFull (Fifo)) {
1355 return EFI_OUT_OF_RESOURCES;
1356 }
1357
1358 //
1359 // FIFO is not full can add data
1360 //
1361 Fifo->Data[Fifo->Last] = Data;
1362 Fifo->Surplus--;
1363 Fifo->Last++;
1364 if (Fifo->Last >= SERIAL_MAX_BUFFER_SIZE) {
1365 Fifo->Last = 0;
1366 }
1367
1368 return EFI_SUCCESS;
1369 }
1370
1371 EFI_STATUS
1372 IsaSerialFifoRemove (
1373 IN SERIAL_DEV_FIFO *Fifo,
1374 OUT UINT8 *Data
1375 )
1376 /*++
1377
1378 Routine Description:
1379 Remove data from specific FIFO
1380
1381 Arguments:
1382 Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO
1383 Data UINT8*: the data removed from FIFO
1384
1385 Returns:
1386 EFI_SUCCESS: Remove data from specific FIFO successfully
1387 EFI_OUT_RESOURCE: Failed to remove data because FIFO is empty
1388
1389 --*/
1390 // TODO: EFI_OUT_OF_RESOURCES - add return value to function comment
1391 {
1392 //
1393 // if FIFO is empty, no data can remove
1394 //
1395 if (IsaSerialFifoEmpty (Fifo)) {
1396 return EFI_OUT_OF_RESOURCES;
1397 }
1398
1399 //
1400 // FIFO is not empty, can remove data
1401 //
1402 *Data = Fifo->Data[Fifo->First];
1403 Fifo->Surplus++;
1404 Fifo->First++;
1405 if (Fifo->First >= SERIAL_MAX_BUFFER_SIZE) {
1406 Fifo->First = 0;
1407 }
1408
1409 return EFI_SUCCESS;
1410 }