]> git.proxmox.com Git - mirror_edk2.git/blame - OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c
Fix typo in GUID declarations
[mirror_edk2.git] / OptionRomPkg / CirrusLogic5430Dxe / CirrusLogic5430GraphicsOutput.c
CommitLineData
4e768df6 1/** @file\r
31f9e631 2Copyright (c) 2007, Intel Corporation\r
3All rights reserved. This program and the accompanying materials\r
4are licensed and made available under the terms and conditions of the BSD License\r
5which accompanies this distribution. The full text of the license may be found at\r
6http://opensource.org/licenses/bsd-license.php\r
7\r
8THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
9WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
87f8ccbe 10\r
11Module Name:\r
12\r
13 UefiCirrusLogic5430GraphicsOutput.c\r
14\r
15Abstract:\r
16\r
31f9e631 17 This file produces the graphics abstration of Graphics Output Protocol. It is called by\r
18 CirrusLogic5430.c file which deals with the EFI 1.1 driver model.\r
87f8ccbe 19 This file just does graphics.\r
20\r
4e768df6 21**/\r
87f8ccbe 22#include "CirrusLogic5430.h"\r
23\r
24//\r
25// Graphics Output Protocol Member Functions\r
26//\r
27EFI_STATUS\r
28EFIAPI\r
29CirrusLogic5430GraphicsOutputQueryMode (\r
30 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
31 IN UINT32 ModeNumber,\r
32 OUT UINTN *SizeOfInfo,\r
33 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info\r
34 )\r
35/*++\r
36\r
37Routine Description:\r
38\r
39 Graphics Output protocol interface to query video mode\r
40\r
41 Arguments:\r
42 This - Protocol instance pointer.\r
43 ModeNumber - The mode number to return information on.\r
44 Info - Caller allocated buffer that returns information about ModeNumber.\r
45 SizeOfInfo - A pointer to the size, in bytes, of the Info buffer.\r
46\r
47 Returns:\r
48 EFI_SUCCESS - Mode information returned.\r
49 EFI_BUFFER_TOO_SMALL - The Info buffer was too small.\r
50 EFI_DEVICE_ERROR - A hardware error occurred trying to retrieve the video mode.\r
51 EFI_NOT_STARTED - Video display is not initialized. Call SetMode ()\r
52 EFI_INVALID_PARAMETER - One of the input args was NULL.\r
53\r
54--*/\r
55{\r
56 CIRRUS_LOGIC_5430_PRIVATE_DATA *Private;\r
57\r
58 Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);\r
59\r
60 if (Private->HardwareNeedsStarting) {\r
61 return EFI_NOT_STARTED;\r
62 }\r
63\r
64 if (Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) {\r
65 return EFI_INVALID_PARAMETER;\r
66 }\r
67\r
68 *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));\r
69 if (*Info == NULL) {\r
70 return EFI_OUT_OF_RESOURCES;\r
71 }\r
72\r
73 *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);\r
74\r
75 (*Info)->Version = 0;\r
76 (*Info)->HorizontalResolution = Private->ModeData[ModeNumber].HorizontalResolution;\r
77 (*Info)->VerticalResolution = Private->ModeData[ModeNumber].VerticalResolution;\r
78 (*Info)->PixelFormat = PixelBltOnly;\r
79 (*Info)->PixelsPerScanLine = (*Info)->HorizontalResolution;\r
80\r
81 return EFI_SUCCESS;\r
82}\r
83\r
84EFI_STATUS\r
85EFIAPI\r
86CirrusLogic5430GraphicsOutputSetMode (\r
87 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
88 IN UINT32 ModeNumber\r
89 )\r
90/*++\r
91\r
92Routine Description:\r
93\r
94 Graphics Output protocol interface to set video mode\r
95\r
96 Arguments:\r
97 This - Protocol instance pointer.\r
98 ModeNumber - The mode number to be set.\r
99\r
100 Returns:\r
101 EFI_SUCCESS - Graphics mode was changed.\r
102 EFI_DEVICE_ERROR - The device had an error and could not complete the request.\r
103 EFI_UNSUPPORTED - ModeNumber is not supported by this device.\r
104\r
105--*/\r
106{\r
107 CIRRUS_LOGIC_5430_PRIVATE_DATA *Private;\r
108 CIRRUS_LOGIC_5430_MODE_DATA *ModeData;\r
109\r
110 Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);\r
111\r
112 if (ModeNumber >= This->Mode->MaxMode) {\r
113 return EFI_UNSUPPORTED;\r
114 }\r
115\r
116 ModeData = &Private->ModeData[ModeNumber];\r
117\r
118 if (Private->LineBuffer) {\r
119 gBS->FreePool (Private->LineBuffer);\r
120 }\r
121\r
122 Private->LineBuffer = NULL;\r
123 Private->LineBuffer = AllocatePool (ModeData->HorizontalResolution);\r
124 if (Private->LineBuffer == NULL) {\r
125 return EFI_OUT_OF_RESOURCES;\r
126 }\r
127\r
31f9e631 128 InitializeGraphicsMode (Private, &CirrusLogic5430VideoModes[ModeData->ModeNumber]);\r
87f8ccbe 129\r
130 This->Mode->Mode = ModeNumber;\r
131 This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution;\r
132 This->Mode->Info->VerticalResolution = ModeData->VerticalResolution;\r
133 This->Mode->Info->PixelFormat = PixelBltOnly;\r
134 This->Mode->Info->PixelsPerScanLine = ModeData->HorizontalResolution;\r
135 This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);\r
136\r
137 This->Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS)(UINTN)NULL;\r
138 This->Mode->FrameBufferSize = 0;\r
139\r
140 Private->HardwareNeedsStarting = FALSE;\r
141\r
142 return EFI_SUCCESS;\r
143}\r
144\r
145EFI_STATUS\r
146EFIAPI\r
147CirrusLogic5430GraphicsOutputBlt (\r
148 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
149 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL\r
150 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,\r
151 IN UINTN SourceX,\r
152 IN UINTN SourceY,\r
153 IN UINTN DestinationX,\r
154 IN UINTN DestinationY,\r
155 IN UINTN Width,\r
156 IN UINTN Height,\r
157 IN UINTN Delta\r
158 )\r
159/*++\r
160\r
161Routine Description:\r
162\r
163 Graphics Output protocol instance to block transfer for CirrusLogic device\r
164\r
165Arguments:\r
166\r
167 This - Pointer to Graphics Output protocol instance\r
168 BltBuffer - The data to transfer to screen\r
169 BltOperation - The operation to perform\r
170 SourceX - The X coordinate of the source for BltOperation\r
171 SourceY - The Y coordinate of the source for BltOperation\r
172 DestinationX - The X coordinate of the destination for BltOperation\r
173 DestinationY - The Y coordinate of the destination for BltOperation\r
174 Width - The width of a rectangle in the blt rectangle in pixels\r
175 Height - The height of a rectangle in the blt rectangle in pixels\r
176 Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.\r
177 If a Delta of 0 is used, the entire BltBuffer will be operated on.\r
178 If a subrectangle of the BltBuffer is used, then Delta represents\r
179 the number of bytes in a row of the BltBuffer.\r
180\r
181Returns:\r
182\r
183 EFI_INVALID_PARAMETER - Invalid parameter passed in\r
184 EFI_SUCCESS - Blt operation success\r
185\r
186--*/\r
187{\r
188 CIRRUS_LOGIC_5430_PRIVATE_DATA *Private;\r
189 EFI_TPL OriginalTPL;\r
190 UINTN DstY;\r
191 UINTN SrcY;\r
192 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;\r
193 UINTN X;\r
194 UINT8 Pixel;\r
195 UINT32 WidePixel;\r
196 UINTN ScreenWidth;\r
197 UINTN Offset;\r
198 UINTN SourceOffset;\r
199 UINT32 CurrentMode;\r
200\r
201 Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);\r
202\r
203 if ((BltOperation < 0) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) {\r
204 return EFI_INVALID_PARAMETER;\r
205 }\r
206\r
207 if (Width == 0 || Height == 0) {\r
208 return EFI_INVALID_PARAMETER;\r
209 }\r
210\r
211 //\r
212 // If Delta is zero, then the entire BltBuffer is being used, so Delta\r
213 // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size,\r
214 // the number of bytes in each row can be computed.\r
215 //\r
216 if (Delta == 0) {\r
217 Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);\r
218 }\r
219\r
220 //\r
221 // We need to fill the Virtual Screen buffer with the blt data.\r
222 // The virtual screen is upside down, as the first row is the bootom row of\r
223 // the image.\r
224 //\r
225\r
226 CurrentMode = This->Mode->Mode;\r
227 //\r
228 // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters\r
229 // are valid for the operation and the current screen geometry.\r
230 //\r
231 if (BltOperation == EfiBltVideoToBltBuffer) {\r
232 //\r
233 // Video to BltBuffer: Source is Video, destination is BltBuffer\r
234 //\r
235 if (SourceY + Height > Private->ModeData[CurrentMode].VerticalResolution) {\r
236 return EFI_INVALID_PARAMETER;\r
237 }\r
238\r
239 if (SourceX + Width > Private->ModeData[CurrentMode].HorizontalResolution) {\r
240 return EFI_INVALID_PARAMETER;\r
241 }\r
242 } else {\r
243 //\r
244 // BltBuffer to Video: Source is BltBuffer, destination is Video\r
245 //\r
246 if (DestinationY + Height > Private->ModeData[CurrentMode].VerticalResolution) {\r
247 return EFI_INVALID_PARAMETER;\r
248 }\r
249\r
250 if (DestinationX + Width > Private->ModeData[CurrentMode].HorizontalResolution) {\r
251 return EFI_INVALID_PARAMETER;\r
252 }\r
253 }\r
254 //\r
255 // We have to raise to TPL Notify, so we make an atomic write the frame buffer.\r
256 // We would not want a timer based event (Cursor, ...) to come in while we are\r
257 // doing this operation.\r
258 //\r
259 OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);\r
260\r
261 switch (BltOperation) {\r
262 case EfiBltVideoToBltBuffer:\r
263 //\r
264 // Video to BltBuffer: Source is Video, destination is BltBuffer\r
265 //\r
266 for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) {\r
267\r
268 Offset = (SrcY * Private->ModeData[CurrentMode].HorizontalResolution) + SourceX;\r
269 if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {\r
270 Private->PciIo->Mem.Read (\r
271 Private->PciIo,\r
272 EfiPciIoWidthUint32,\r
273 0,\r
274 Offset,\r
275 Width >> 2,\r
276 Private->LineBuffer\r
277 );\r
278 } else {\r
279 Private->PciIo->Mem.Read (\r
280 Private->PciIo,\r
281 EfiPciIoWidthUint8,\r
282 0,\r
283 Offset,\r
284 Width,\r
285 Private->LineBuffer\r
286 );\r
287 }\r
288\r
289 for (X = 0; X < Width; X++) {\r
290 Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));\r
291\r
292 Blt->Red = (UINT8) (Private->LineBuffer[X] & 0xe0);\r
293 Blt->Green = (UINT8) ((Private->LineBuffer[X] & 0x1c) << 3);\r
294 Blt->Blue = (UINT8) ((Private->LineBuffer[X] & 0x03) << 6);\r
295 }\r
296 }\r
297 break;\r
298\r
299 case EfiBltVideoToVideo:\r
300 //\r
301 // Perform hardware acceleration for Video to Video operations\r
302 //\r
303 ScreenWidth = Private->ModeData[CurrentMode].HorizontalResolution;\r
304 SourceOffset = (SourceY * Private->ModeData[CurrentMode].HorizontalResolution) + (SourceX);\r
305 Offset = (DestinationY * Private->ModeData[CurrentMode].HorizontalResolution) + (DestinationX);\r
306\r
307 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0000);\r
308 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0010);\r
309 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0012);\r
310 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0014);\r
311\r
312 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0001);\r
313 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0011);\r
314 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0013);\r
315 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0015);\r
316\r
317 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Width << 8) & 0xff00) | 0x20));\r
318 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Width & 0xff00) | 0x21));\r
319 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Height << 8) & 0xff00) | 0x22));\r
320 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Height & 0xff00) | 0x23));\r
321 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x24));\r
322 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x25));\r
323 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x26));\r
324 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x27));\r
325 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) << 8) & 0xff00) | 0x28));\r
326 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 0) & 0xff00) | 0x29));\r
327 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 8) & 0xff00) | 0x2a));\r
328 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) << 8) & 0xff00) | 0x2c));\r
329 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 0) & 0xff00) | 0x2d));\r
330 outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 8) & 0xff00) | 0x2e));\r
331 outw (Private, GRAPH_ADDRESS_REGISTER, 0x002f);\r
332 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0030);\r
333 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0d32);\r
334 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0033);\r
335 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0034);\r
336 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0035);\r
337\r
338 outw (Private, GRAPH_ADDRESS_REGISTER, 0x0231);\r
339\r
340 outb (Private, GRAPH_ADDRESS_REGISTER, 0x31);\r
341 while ((inb (Private, GRAPH_DATA_REGISTER) & 0x01) == 0x01)\r
342 ;\r
343 break;\r
344\r
345 case EfiBltVideoFill:\r
346 Blt = BltBuffer;\r
347 Pixel = (UINT8) ((Blt->Red & 0xe0) | ((Blt->Green >> 3) & 0x1c) | ((Blt->Blue >> 6) & 0x03));\r
348 WidePixel = (Pixel << 8) | Pixel;\r
349 WidePixel = (WidePixel << 16) | WidePixel;\r
350\r
351 if (DestinationX == 0 && Width == Private->ModeData[CurrentMode].HorizontalResolution) {\r
352 Offset = DestinationY * Private->ModeData[CurrentMode].HorizontalResolution;\r
353 if (((Offset & 0x03) == 0) && (((Width * Height) & 0x03) == 0)) {\r
354 Private->PciIo->Mem.Write (\r
355 Private->PciIo,\r
356 EfiPciIoWidthFillUint32,\r
357 0,\r
358 Offset,\r
359 (Width * Height) >> 2,\r
360 &WidePixel\r
361 );\r
362 } else {\r
363 Private->PciIo->Mem.Write (\r
364 Private->PciIo,\r
365 EfiPciIoWidthFillUint8,\r
366 0,\r
367 Offset,\r
368 Width * Height,\r
369 &Pixel\r
370 );\r
371 }\r
372 } else {\r
373 for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {\r
374 Offset = (DstY * Private->ModeData[CurrentMode].HorizontalResolution) + DestinationX;\r
375 if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {\r
376 Private->PciIo->Mem.Write (\r
377 Private->PciIo,\r
378 EfiPciIoWidthFillUint32,\r
379 0,\r
380 Offset,\r
381 Width >> 2,\r
382 &WidePixel\r
383 );\r
384 } else {\r
385 Private->PciIo->Mem.Write (\r
386 Private->PciIo,\r
387 EfiPciIoWidthFillUint8,\r
388 0,\r
389 Offset,\r
390 Width,\r
391 &Pixel\r
392 );\r
393 }\r
394 }\r
395 }\r
396 break;\r
397\r
398 case EfiBltBufferToVideo:\r
399 for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {\r
400\r
401 for (X = 0; X < Width; X++) {\r
402 Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (SrcY * Delta) + (SourceX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));\r
403 Private->LineBuffer[X] = (UINT8) ((Blt->Red & 0xe0) | ((Blt->Green >> 3) & 0x1c) | ((Blt->Blue >> 6) & 0x03));\r
404 }\r
405\r
406 Offset = (DstY * Private->ModeData[CurrentMode].HorizontalResolution) + DestinationX;\r
407\r
408 if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {\r
409 Private->PciIo->Mem.Write (\r
410 Private->PciIo,\r
411 EfiPciIoWidthUint32,\r
412 0,\r
413 Offset,\r
414 Width >> 2,\r
415 Private->LineBuffer\r
416 );\r
417 } else {\r
418 Private->PciIo->Mem.Write (\r
419 Private->PciIo,\r
420 EfiPciIoWidthUint8,\r
421 0,\r
422 Offset,\r
423 Width,\r
424 Private->LineBuffer\r
425 );\r
426 }\r
427 }\r
428 break;\r
429 default:\r
430 ASSERT (FALSE);\r
431 }\r
432\r
433 gBS->RestoreTPL (OriginalTPL);\r
434\r
435 return EFI_SUCCESS;\r
436}\r
437\r
438EFI_STATUS\r
439CirrusLogic5430GraphicsOutputConstructor (\r
440 CIRRUS_LOGIC_5430_PRIVATE_DATA *Private\r
441 )\r
442{\r
443 EFI_STATUS Status;\r
444 EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;\r
87f8ccbe 445\r
446\r
447 GraphicsOutput = &Private->GraphicsOutput;\r
448 GraphicsOutput->QueryMode = CirrusLogic5430GraphicsOutputQueryMode;\r
449 GraphicsOutput->SetMode = CirrusLogic5430GraphicsOutputSetMode;\r
450 GraphicsOutput->Blt = CirrusLogic5430GraphicsOutputBlt;\r
451\r
452 //\r
453 // Initialize the private data\r
454 //\r
455 Status = gBS->AllocatePool (\r
456 EfiBootServicesData,\r
457 sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE),\r
458 (VOID **) &Private->GraphicsOutput.Mode\r
459 );\r
460 if (EFI_ERROR (Status)) {\r
461 return Status;\r
462 }\r
463 Status = gBS->AllocatePool (\r
464 EfiBootServicesData,\r
465 sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),\r
466 (VOID **) &Private->GraphicsOutput.Mode->Info\r
467 );\r
468 if (EFI_ERROR (Status)) {\r
469 return Status;\r
470 }\r
31f9e631 471 Private->GraphicsOutput.Mode->MaxMode = (UINT32) Private->MaxMode;\r
472 Private->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER;\r
473 Private->HardwareNeedsStarting = TRUE;\r
474 Private->LineBuffer = NULL;\r
87f8ccbe 475\r
476 //\r
477 // Initialize the hardware\r
478 //\r
479 GraphicsOutput->SetMode (GraphicsOutput, 0);\r
480 DrawLogo (\r
481 Private,\r
482 Private->ModeData[Private->GraphicsOutput.Mode->Mode].HorizontalResolution,\r
483 Private->ModeData[Private->GraphicsOutput.Mode->Mode].VerticalResolution\r
484 );\r
485\r
486 return EFI_SUCCESS;\r
487}\r
488\r
489EFI_STATUS\r
490CirrusLogic5430GraphicsOutputDestructor (\r
491 CIRRUS_LOGIC_5430_PRIVATE_DATA *Private\r
492 )\r
493/*++\r
494\r
495Routine Description:\r
496\r
497Arguments:\r
498\r
499Returns:\r
500\r
501 None\r
502\r
503--*/\r
504{\r
505 if (Private->GraphicsOutput.Mode != NULL) {\r
506 if (Private->GraphicsOutput.Mode->Info != NULL) {\r
507 gBS->FreePool (Private->GraphicsOutput.Mode->Info);\r
508 }\r
509 gBS->FreePool (Private->GraphicsOutput.Mode);\r
510 }\r
511\r
512 return EFI_SUCCESS;\r
513}\r
514\r
515\r