Move Pci/AtapiPassThru/Dxe to Pci/AtapiPassThruDxe.
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / AtapiPassThruDxe / AtapiPassThru.c
CommitLineData
513f3f44 1/** @file\r
2 Copyright (c) 2006, Intel Corporation \r
3 All rights reserved. This program and the accompanying materials \r
4 are licensed and made available under the terms and conditions of the BSD License \r
5 which accompanies this distribution. The full text of the license may be found at \r
6 http://opensource.org/licenses/bsd-license.php \r
7\r
8 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
9 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
10\r
11**/\r
12\r
13#include "AtapiPassThru.h"\r
14\r
15///\r
16/// IDE registers' fixed address\r
17///\r
18static IDE_BASE_REGISTERS gAtapiIoPortRegisters[2] = {\r
19 { 0x1f0, { 0x1f1 }, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, { 0x1f7 }, { 0x3f6 }, 0x3f7, 0 },\r
20 { 0x170, { 0x171 }, 0x172, 0x173, 0x174, 0x175, 0x176, { 0x177 }, { 0x376 }, 0x377, 0 } \r
21};\r
22\r
23static SCSI_COMMAND_SET gEndTable = { 0xff, (DATA_DIRECTION) 0xff };\r
24\r
25///\r
26/// This table contains all the supported ATAPI commands.\r
27///\r
28static SCSI_COMMAND_SET gSupportedATAPICommands[] = {\r
29 { OP_INQUIRY, DataIn },\r
30 { OP_LOAD_UNLOAD_CD, NoData },\r
31 { OP_MECHANISM_STATUS, DataIn },\r
32 { OP_MODE_SELECT_10, DataOut },\r
33 { OP_MODE_SENSE_10, DataIn },\r
34 { OP_PAUSE_RESUME, NoData },\r
35 { OP_PLAY_AUDIO_10, DataIn },\r
36 { OP_PLAY_AUDIO_MSF, DataIn },\r
37 { OP_PLAY_CD, DataIn },\r
38 { OP_PLAY_CD_MSF, DataIn },\r
39 { OP_PREVENT_ALLOW_MEDIUM_REMOVAL,NoData },\r
40 { OP_READ_10, DataIn },\r
41 { OP_READ_12, DataIn },\r
42 { OP_READ_CAPACITY, DataIn },\r
43 { OP_READ_CD, DataIn },\r
44 { OP_READ_CD_MSF, DataIn },\r
45 { OP_READ_HEADER, DataIn },\r
46 { OP_READ_SUB_CHANNEL, DataIn },\r
47 { OP_READ_TOC, DataIn },\r
48 { OP_REQUEST_SENSE, DataIn },\r
49 { OP_SCAN, NoData },\r
50 { OP_SEEK_10, NoData },\r
51 { OP_SET_CD_SPEED, DataOut },\r
52 { OP_STOPPLAY_SCAN, NoData },\r
53 { OP_START_STOP_UNIT, NoData },\r
54 { OP_TEST_UNIT_READY, NoData },\r
55 { OP_FORMAT_UNIT, DataOut },\r
56 { OP_READ_FORMAT_CAPACITIES, DataIn },\r
57 { OP_VERIFY, DataOut },\r
58 { OP_WRITE_10, DataOut },\r
59 { OP_WRITE_12, DataOut },\r
60 { OP_WRITE_AND_VERIFY, DataOut },\r
61 { 0xff, (DATA_DIRECTION) 0xff } \r
62};\r
63\r
64static CHAR16 *gControllerNameString = (CHAR16 *) L"ATAPI Controller";\r
65static CHAR16 *gAtapiChannelString = (CHAR16 *) L"ATAPI Channel";\r
66\r
67EFI_DRIVER_BINDING_PROTOCOL gAtapiScsiPassThruDriverBinding = {\r
68 AtapiScsiPassThruDriverBindingSupported,\r
69 AtapiScsiPassThruDriverBindingStart,\r
70 AtapiScsiPassThruDriverBindingStop,\r
71 0xa,\r
72 NULL,\r
73 NULL\r
74};\r
75\r
76/**\r
77 Supported.\r
78\r
79 (Standard DriverBinding Protocol Supported() function)\r
80\r
81 @return EFI_STATUS\r
82\r
83 @todo This - add argument and description to function comment\r
84 @todo Controller - add argument and description to function comment\r
85 @todo RemainingDevicePath - add argument and description to function comment\r
86 @todo EFI_UNSUPPORTED - add return value to function comment\r
87**/\r
88EFI_STATUS\r
89EFIAPI\r
90AtapiScsiPassThruDriverBindingSupported (\r
91 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
92 IN EFI_HANDLE Controller,\r
93 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
94 )\r
95{\r
96 EFI_STATUS Status;\r
97 EFI_PCI_IO_PROTOCOL *PciIo;\r
98 PCI_TYPE00 Pci;\r
99\r
100 //\r
101 // Open the IO Abstraction(s) needed to perform the supported test\r
102 //\r
103 Status = gBS->OpenProtocol (\r
104 Controller,\r
105 &gEfiPciIoProtocolGuid,\r
106 (VOID **) &PciIo,\r
107 This->DriverBindingHandle,\r
108 Controller,\r
109 EFI_OPEN_PROTOCOL_BY_DRIVER\r
110 );\r
111 if (EFI_ERROR (Status)) {\r
112 return Status;\r
113 }\r
114 //\r
115 // Use the PCI I/O Protocol to see if Controller is a IDE Controller that\r
116 // can be managed by this driver. Read the PCI Configuration Header\r
117 // for this device.\r
118 //\r
119 Status = PciIo->Pci.Read (\r
120 PciIo,\r
121 EfiPciIoWidthUint32,\r
122 0,\r
123 sizeof (Pci) / sizeof (UINT32),\r
124 &Pci\r
125 );\r
126 if (EFI_ERROR (Status)) {\r
127 gBS->CloseProtocol (\r
128 Controller,\r
129 &gEfiPciIoProtocolGuid,\r
130 This->DriverBindingHandle,\r
131 Controller\r
132 );\r
133 return EFI_UNSUPPORTED;\r
134 }\r
135\r
136 if (Pci.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE || Pci.Hdr.ClassCode[1] != PCI_CLASS_IDE) {\r
137\r
138 Status = EFI_UNSUPPORTED;\r
139 }\r
140\r
141 gBS->CloseProtocol (\r
142 Controller,\r
143 &gEfiPciIoProtocolGuid,\r
144 This->DriverBindingHandle,\r
145 Controller\r
146 );\r
147\r
148 return Status;\r
149}\r
150\r
151/**\r
152 Create handles for IDE channels specified by RemainingDevicePath.\r
153 Install SCSI Pass Thru Protocol onto each created handle.\r
154\r
155 (Standard DriverBinding Protocol Start() function)\r
156\r
157 @return EFI_STATUS\r
158\r
159 @todo This - add argument and description to function comment\r
160 @todo Controller - add argument and description to function comment\r
161 @todo RemainingDevicePath - add argument and description to function comment\r
162**/\r
163EFI_STATUS\r
164EFIAPI\r
165AtapiScsiPassThruDriverBindingStart (\r
166 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
167 IN EFI_HANDLE Controller,\r
168 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
169 )\r
170{\r
171 EFI_STATUS Status;\r
172 EFI_PCI_IO_PROTOCOL *PciIo;\r
173\r
174 PciIo = NULL;\r
175 Status = gBS->OpenProtocol (\r
176 Controller,\r
177 &gEfiPciIoProtocolGuid,\r
178 (VOID **) &PciIo,\r
179 This->DriverBindingHandle,\r
180 Controller,\r
181 EFI_OPEN_PROTOCOL_BY_DRIVER\r
182 );\r
183 if (EFI_ERROR (Status)) {\r
184 return Status;\r
185 }\r
186\r
187 Status = PciIo->Attributes (\r
188 PciIo,\r
189 EfiPciIoAttributeOperationEnable,\r
190 EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE,\r
191 NULL\r
192 );\r
193 if (EFI_ERROR (Status)) {\r
194 goto Done;\r
195 }\r
196\r
197 //\r
198 // Create SCSI Pass Thru instance for the IDE channel.\r
199 //\r
200 Status = RegisterAtapiScsiPassThru (This, Controller, PciIo);\r
201\r
202Done:\r
203 if (EFI_ERROR (Status)) {\r
204 if (PciIo) {\r
205 PciIo->Attributes (\r
206 PciIo,\r
207 EfiPciIoAttributeOperationDisable,\r
208 EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE,\r
209 NULL\r
210 );\r
211 }\r
212\r
213 gBS->CloseProtocol (\r
214 Controller,\r
215 &gEfiPciIoProtocolGuid,\r
216 This->DriverBindingHandle,\r
217 Controller\r
218 );\r
219 }\r
220\r
221 return Status;\r
222}\r
223\r
224/**\r
225 Stop.\r
226\r
227 (Standard DriverBinding Protocol Stop() function)\r
228\r
229 @return EFI_STATUS\r
230\r
231 @todo This - add argument and description to function comment\r
232 @todo Controller - add argument and description to function comment\r
233 @todo NumberOfChildren - add argument and description to function comment\r
234 @todo ChildHandleBuffer - add argument and description to function comment\r
235 @todo EFI_SUCCESS - add return value to function comment\r
236**/\r
237EFI_STATUS\r
238EFIAPI\r
239AtapiScsiPassThruDriverBindingStop (\r
240 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
241 IN EFI_HANDLE Controller,\r
242 IN UINTN NumberOfChildren,\r
243 IN EFI_HANDLE *ChildHandleBuffer\r
244 )\r
245{\r
246 EFI_STATUS Status;\r
247 EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru;\r
248 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate;\r
249\r
250 Status = gBS->OpenProtocol (\r
251 Controller,\r
252 &gEfiScsiPassThruProtocolGuid,\r
253 (VOID **) &ScsiPassThru,\r
254 This->DriverBindingHandle,\r
255 Controller,\r
256 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
257 );\r
258 if (EFI_ERROR (Status)) {\r
259 return Status;\r
260 }\r
261\r
262 AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (ScsiPassThru);\r
263\r
264 Status = gBS->UninstallProtocolInterface (\r
265 Controller,\r
266 &gEfiScsiPassThruProtocolGuid,\r
267 &AtapiScsiPrivate->ScsiPassThru\r
268 );\r
269 if (EFI_ERROR (Status)) {\r
270 return Status;\r
271 }\r
272 //\r
273 // Release Pci Io protocol on the controller handle.\r
274 //\r
275 AtapiScsiPrivate->PciIo->Attributes (\r
276 AtapiScsiPrivate->PciIo,\r
277 EfiPciIoAttributeOperationDisable,\r
278 EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE,\r
279 NULL\r
280 );\r
281\r
282 gBS->CloseProtocol (\r
283 Controller,\r
284 &gEfiPciIoProtocolGuid,\r
285 This->DriverBindingHandle,\r
286 Controller\r
287 );\r
288\r
289 gBS->FreePool (AtapiScsiPrivate);\r
290\r
291 return EFI_SUCCESS;\r
292}\r
293\r
294/**\r
295 Attaches SCSI Pass Thru Protocol for specified IDE channel.\r
296\r
297 @param Controller: Parent device handle to the IDE channel.\r
298 @param PciIo: PCI I/O protocol attached on the "Controller".\r
299\r
300 @return EFI_SUCCESS Always returned unless installing SCSI Pass Thru Protocol failed.\r
301\r
302 @todo This - add argument and description to function comment\r
303 @todo Controller - add argument and description to function comment\r
304 @todo PciIo - add argument and description to function comment\r
305 @todo EFI_OUT_OF_RESOURCES - add return value to function comment\r
306**/\r
307EFI_STATUS\r
308RegisterAtapiScsiPassThru (\r
309 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
310 IN EFI_HANDLE Controller,\r
311 IN EFI_PCI_IO_PROTOCOL *PciIo\r
312 )\r
313{\r
314 EFI_STATUS Status;\r
315 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate;\r
316 UINT64 Attributes;\r
317\r
318 AtapiScsiPrivate = AllocateZeroPool (sizeof (ATAPI_SCSI_PASS_THRU_DEV));\r
319 if (AtapiScsiPrivate == NULL) {\r
320 return EFI_OUT_OF_RESOURCES;\r
321 }\r
322\r
323 Attributes = EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE;\r
324 CopyMem (AtapiScsiPrivate->ChannelName, gAtapiChannelString, sizeof (gAtapiChannelString));\r
325\r
326 //\r
327 // Enable channel\r
328 //\r
329 PciIo->Attributes (PciIo, EfiPciIoAttributeOperationSet, Attributes, NULL);\r
330\r
331 AtapiScsiPrivate->Signature = ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE;\r
332 AtapiScsiPrivate->Handle = Controller;\r
333\r
334 //\r
335 // will reset the IoPort inside each API function.\r
336 //\r
337 AtapiScsiPrivate->IoPort = gAtapiIoPortRegisters;\r
338 AtapiScsiPrivate->PciIo = PciIo;\r
339\r
340 //\r
341 // initialize SCSI Pass Thru Protocol interface\r
342 //\r
343 AtapiScsiPrivate->ScsiPassThru.Mode = &AtapiScsiPrivate->ScsiPassThruMode;\r
344 AtapiScsiPrivate->ScsiPassThru.PassThru = AtapiScsiPassThruFunction;\r
345 AtapiScsiPrivate->ScsiPassThru.GetNextDevice = AtapiScsiPassThruGetNextDevice;\r
346 AtapiScsiPrivate->ScsiPassThru.BuildDevicePath = AtapiScsiPassThruBuildDevicePath;\r
347 AtapiScsiPrivate->ScsiPassThru.GetTargetLun = AtapiScsiPassThruGetTargetLun;\r
348 AtapiScsiPrivate->ScsiPassThru.ResetChannel = AtapiScsiPassThruResetChannel;\r
349 AtapiScsiPrivate->ScsiPassThru.ResetTarget = AtapiScsiPassThruResetTarget;\r
350\r
351 //\r
352 // Set Mode\r
353 //\r
354 CopyMem (AtapiScsiPrivate->ControllerName, gControllerNameString, sizeof (gControllerNameString));\r
355\r
356 AtapiScsiPrivate->ScsiPassThruMode.ControllerName = AtapiScsiPrivate->ControllerName;\r
357 AtapiScsiPrivate->ScsiPassThruMode.ChannelName = AtapiScsiPrivate->ChannelName;\r
358 AtapiScsiPrivate->ScsiPassThruMode.AdapterId = 4;\r
359 //\r
360 // non-RAID SCSI controllers should set both physical and logical attributes\r
361 //\r
362 AtapiScsiPrivate->ScsiPassThruMode.Attributes = EFI_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | \r
363 EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
364 AtapiScsiPrivate->ScsiPassThruMode.IoAlign = 0;\r
365\r
366 //\r
367 // Initialize the LatestTargetId to 0xFFFFFFFF (for the GetNextDevice() call).\r
368 //\r
369 AtapiScsiPrivate->LatestTargetId = 0xFFFFFFFF;\r
370 AtapiScsiPrivate->LatestLun = 0;\r
371\r
372 Status = gBS->InstallProtocolInterface (\r
373 &Controller,\r
374 &gEfiScsiPassThruProtocolGuid,\r
375 EFI_NATIVE_INTERFACE,\r
376 &AtapiScsiPrivate->ScsiPassThru\r
377 );\r
378 return Status;\r
379}\r
380\r
381/**\r
382 Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function.\r
383\r
384 @param This The EFI_SCSI_PASS_THRU_PROTOCOL instance.\r
385 @param Target The Target ID of the ATAPI device to send the SCSI\r
386 Request Packet. To ATAPI devices attached on an IDE\r
387 Channel, Target ID 0 indicates Master device;Target\r
388 ID 1 indicates Slave device.\r
389 @param Lun The LUN of the ATAPI device to send the SCSI Request\r
390 Packet. To the ATAPI device, Lun is always 0.\r
391 @param Packet The SCSI Request Packet to send to the ATAPI device\r
392 specified by Target and Lun.\r
393 @param Event If non-blocking I/O is not supported then Event is ignored,\r
394 and blocking I/O is performed.<br>\r
395 If Event is NULL, then blocking I/O is performed.<br>\r
396 If Event is not NULL and non blocking I/O is supported,\r
397 then non-blocking I/O is performed, and Event will be signaled\r
398 when the SCSI Request Packet completes.\r
399\r
400 @todo This - add argument and description to function comment\r
401 @todo EFI_INVALID_PARAMETER - add return value to function comment\r
402 @todo EFI_SUCCESS - add return value to function comment\r
403**/\r
404EFI_STATUS\r
405EFIAPI\r
406AtapiScsiPassThruFunction (\r
407 IN EFI_SCSI_PASS_THRU_PROTOCOL *This,\r
408 IN UINT32 Target,\r
409 IN UINT64 Lun,\r
410 IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
411 IN EFI_EVENT Event OPTIONAL\r
412 )\r
413{\r
414 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate;\r
415 EFI_STATUS Status;\r
416\r
417 AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
418\r
419 //\r
420 // Target is not allowed beyond MAX_TARGET_ID\r
421 //\r
422 if (Target > MAX_TARGET_ID) {\r
423 return EFI_INVALID_PARAMETER;\r
424 }\r
425 \r
426 //\r
427 // check the data fields in Packet parameter.\r
428 //\r
429 Status = CheckSCSIRequestPacket (Packet);\r
430 if (EFI_ERROR (Status)) {\r
431 return Status;\r
432 }\r
433\r
434 //\r
435 // If Request Packet targets at the IDE channel itself,\r
436 // do nothing.\r
437 //\r
438 if (Target == This->Mode->AdapterId) {\r
439 Packet->TransferLength = 0;\r
440 return EFI_SUCCESS;\r
441 }\r
442 \r
443 //\r
444 // According to Target ID, reset the Atapi I/O Register mapping\r
445 // (Target Id in [0,1] area, using gAtapiIoPortRegisters[0],\r
446 // Target Id in [2,3] area, using gAtapiIoPortRegisters[1]\r
447 //\r
448 if ((Target / 2) == 0) {\r
449 AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[0];\r
450 } else {\r
451 AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[1];\r
452 }\r
453 \r
454 //\r
455 // the ATAPI SCSI interface does not support non-blocking I/O\r
456 // ignore the Event parameter\r
457 //\r
458 // Performs blocking I/O.\r
459 //\r
460 Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, Packet);\r
461 return Status;\r
462}\r
463\r
464/**\r
465 Used to retrieve the list of legal Target IDs for SCSI devices \r
466 on a SCSI channel.\r
467\r
468 @param This Protocol instance pointer.\r
469 @param Target On input, a pointer to the Target ID of a SCSI\r
470 device present on the SCSI channel. On output,\r
471 a pointer to the Target ID of the next SCSI device\r
472 present on a SCSI channel. An input value of\r
473 0xFFFFFFFF retrieves the Target ID of the first\r
474 SCSI device present on a SCSI channel.\r
475 @param Lun On input, a pointer to the LUN of a SCSI device\r
476 present on the SCSI channel. On output, a pointer\r
477 to the LUN of the next SCSI device present on\r
478 a SCSI channel.\r
479\r
480 @retval EFI_SUCCESS The Target ID and Lun of the next SCSI device\r
481 on the SCSI channel was returned in Target and Lun.\r
482 @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.\r
483 @retval EFI_INVALID_PARAMETER Target is not 0xFFFFFFFF,and Target and Lun were not\r
484 returned on a previous call to GetNextDevice().\r
485\r
486**/\r
487EFI_STATUS\r
488EFIAPI\r
489AtapiScsiPassThruGetNextDevice (\r
490 IN EFI_SCSI_PASS_THRU_PROTOCOL *This,\r
491 IN OUT UINT32 *Target,\r
492 IN OUT UINT64 *Lun\r
493 )\r
494{\r
495 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate;\r
496\r
497 //\r
498 // Retrieve Device Private Data Structure.\r
499 //\r
500 AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
501\r
502 //\r
503 // Check whether Target is valid.\r
504 //\r
505 if (Target == NULL || Lun == NULL) {\r
506 return EFI_INVALID_PARAMETER;\r
507 }\r
508\r
509 if ((*Target != 0xFFFFFFFF) &&\r
510 ((*Target != AtapiScsiPrivate->LatestTargetId) ||\r
511 (*Lun != AtapiScsiPrivate->LatestLun))) {\r
512 return EFI_INVALID_PARAMETER;\r
513 }\r
514\r
515 if (*Target == MAX_TARGET_ID) {\r
516 return EFI_NOT_FOUND;\r
517 }\r
518\r
519 if (*Target == 0xFFFFFFFF) {\r
520 *Target = 0;\r
521 } else {\r
522 *Target = AtapiScsiPrivate->LatestTargetId + 1;\r
523 }\r
524\r
525 *Lun = 0;\r
526\r
527 //\r
528 // Update the LatestTargetId.\r
529 //\r
530 AtapiScsiPrivate->LatestTargetId = *Target;\r
531 AtapiScsiPrivate->LatestLun = *Lun;\r
532\r
533 return EFI_SUCCESS;\r
534\r
535}\r
536\r
537/**\r
538 Used to allocate and build a device path node for a SCSI device \r
539 on a SCSI channel. Would not build device path for a SCSI Host Controller.\r
540\r
541 @param This Protocol instance pointer.\r
542 @param Target The Target ID of the SCSI device for which\r
543 a device path node is to be allocated and built.\r
544 @param Lun The LUN of the SCSI device for which a device\r
545 path node is to be allocated and built.\r
546 @param DevicePath A pointer to a single device path node that\r
547 describes the SCSI device specified by\r
548 Target and Lun. This function is responsible\r
549 for allocating the buffer DevicePath with the boot\r
550 service AllocatePool(). It is the caller's\r
551 responsibility to free DevicePath when the caller\r
552 is finished with DevicePath.\r
553\r
554 @retval EFI_SUCCESS The device path node that describes the SCSI device\r
555 specified by Target and Lun was allocated and\r
556 returned in DevicePath.\r
557 @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does\r
558 not exist on the SCSI channel.\r
559 @retval EFI_INVALID_PARAMETER DevicePath is NULL.\r
560 @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate\r
561 DevicePath.\r
562\r
563**/\r
564EFI_STATUS\r
565EFIAPI\r
566AtapiScsiPassThruBuildDevicePath (\r
567 IN EFI_SCSI_PASS_THRU_PROTOCOL *This,\r
568 IN UINT32 Target,\r
569 IN UINT64 Lun,\r
570 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
571 )\r
572{\r
573 EFI_DEV_PATH *Node;\r
574\r
575 //\r
576 // Validate parameters passed in.\r
577 //\r
578 \r
579 if (DevicePath == NULL) {\r
580 return EFI_INVALID_PARAMETER;\r
581 }\r
582 \r
583 //\r
584 // can not build device path for the SCSI Host Controller.\r
585 //\r
586 if ((Target > (MAX_TARGET_ID - 1)) || (Lun != 0)) {\r
587 return EFI_NOT_FOUND;\r
588 }\r
589\r
590 Node = AllocateZeroPool (sizeof (EFI_DEV_PATH));\r
591 if (Node == NULL) {\r
592 return EFI_OUT_OF_RESOURCES;\r
593 }\r
594\r
595 Node->DevPath.Type = MESSAGING_DEVICE_PATH;\r
596 Node->DevPath.SubType = MSG_ATAPI_DP;\r
597 SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH));\r
598\r
599 Node->Atapi.PrimarySecondary = (UINT8) (Target / 2);\r
600 Node->Atapi.SlaveMaster = (UINT8) (Target % 2);\r
601 Node->Atapi.Lun = (UINT16) Lun;\r
602\r
603 *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node;\r
604\r
605 return EFI_SUCCESS;\r
606}\r
607\r
608/**\r
609 Used to translate a device path node to a Target ID and LUN.\r
610\r
611 @param This Protocol instance pointer.\r
612 @param DevicePath A pointer to the device path node that\r
613 describes a SCSI device on the SCSI channel.\r
614 @param Target A pointer to the Target ID of a SCSI device\r
615 on the SCSI channel.\r
616 @param Lun A pointer to the LUN of a SCSI device on\r
617 the SCSI channel.\r
618\r
619 @retval EFI_SUCCESS DevicePath was successfully translated to a\r
620 Target ID and LUN, and they were returned\r
621 in Target and Lun.\r
622 @retval EFI_INVALID_PARAMETER DevicePath is NULL.\r
623 @retval EFI_INVALID_PARAMETER Target is NULL.\r
624 @retval EFI_INVALID_PARAMETER Lun is NULL.\r
625 @retval EFI_UNSUPPORTED This driver does not support the device path\r
626 node type in DevicePath.\r
627 @retval EFI_NOT_FOUND A valid translation from DevicePath to a\r
628 Target ID and LUN does not exist.\r
629\r
630**/\r
631EFI_STATUS\r
632EFIAPI\r
633AtapiScsiPassThruGetTargetLun (\r
634 IN EFI_SCSI_PASS_THRU_PROTOCOL *This,\r
635 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
636 OUT UINT32 *Target,\r
637 OUT UINT64 *Lun\r
638 )\r
639{\r
640 EFI_DEV_PATH *Node;\r
641\r
642 //\r
643 // Validate parameters passed in.\r
644 //\r
645 if (DevicePath == NULL || Target == NULL || Lun == NULL) {\r
646 return EFI_INVALID_PARAMETER;\r
647 }\r
648 \r
649 //\r
650 // Check whether the DevicePath belongs to SCSI_DEVICE_PATH\r
651 //\r
652 if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||\r
653 (DevicePath->SubType != MSG_ATAPI_DP) ||\r
654 (DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) {\r
655 return EFI_UNSUPPORTED;\r
656 }\r
657\r
658 Node = (EFI_DEV_PATH *) DevicePath;\r
659\r
660 *Target = Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster;\r
661 *Lun = Node->Atapi.Lun;\r
662\r
663 if (*Target > (MAX_TARGET_ID - 1) || *Lun != 0) {\r
664 return EFI_NOT_FOUND;\r
665 }\r
666\r
667 return EFI_SUCCESS;\r
668}\r
669\r
670/**\r
671 Resets a SCSI channel.This operation resets all the \r
672 SCSI devices connected to the SCSI channel.\r
673\r
674 @param This Protocol instance pointer.\r
675\r
676 @retval EFI_SUCCESS The SCSI channel was reset.\r
677 @retval EFI_UNSUPPORTED The SCSI channel does not support\r
678 a channel reset operation.\r
679 @retval EFI_DEVICE_ERROR A device error occurred while\r
680 attempting to reset the SCSI channel.\r
681 @retval EFI_TIMEOUT A timeout occurred while attempting\r
682 to reset the SCSI channel.\r
683\r
684**/\r
685EFI_STATUS\r
686EFIAPI\r
687AtapiScsiPassThruResetChannel (\r
688 IN EFI_SCSI_PASS_THRU_PROTOCOL *This\r
689 )\r
690{\r
691 UINT8 DeviceControlValue;\r
692 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate;\r
693 UINT8 Index;\r
694\r
695 AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
696\r
697 //\r
698 // Reset both Primary channel and Secondary channel.\r
699 // so, the IoPort pointer must point to the right I/O Register group\r
700 //\r
701 for (Index = 0; Index < 2; Index++) {\r
702 //\r
703 // Reset\r
704 //\r
705 AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[Index];\r
706\r
707 DeviceControlValue = 0;\r
708 //\r
709 // set SRST bit to initiate soft reset\r
710 //\r
711 DeviceControlValue |= SRST;\r
712 //\r
713 // disable Interrupt\r
714 //\r
715 DeviceControlValue |= bit (1);\r
716 WritePortB (\r
717 AtapiScsiPrivate->PciIo,\r
718 AtapiScsiPrivate->IoPort->Alt.DeviceControl,\r
719 DeviceControlValue\r
720 );\r
721\r
722 //\r
723 // Wait 10us\r
724 //\r
725 gBS->Stall (10);\r
726\r
727 //\r
728 // Clear SRST bit\r
729 // 0xfb:1111,1011\r
730 //\r
731 DeviceControlValue &= 0xfb;\r
732 \r
733 WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue);\r
734\r
735 //\r
736 // slave device needs at most 31s to clear BSY\r
737 //\r
738 if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000) == EFI_TIMEOUT) {\r
739 return EFI_DEVICE_ERROR;\r
740 }\r
741 }\r
742\r
743 return EFI_SUCCESS;\r
744}\r
745\r
746/**\r
747 Resets a SCSI device that is connected to a SCSI channel.\r
748\r
749 @param This Protocol instance pointer.\r
750 @param Target The Target ID of the SCSI device to reset.\r
751 @param Lun The LUN of the SCSI device to reset.\r
752\r
753 @retval EFI_SUCCESS The SCSI device specified by Target and\r
754 Lun was reset.\r
755 @retval EFI_UNSUPPORTED The SCSI channel does not support a target\r
756 reset operation.\r
757 @retval EFI_INVALID_PARAMETER Target or Lun are invalid.\r
758 @retval EFI_DEVICE_ERROR A device error occurred while attempting\r
759 to reset the SCSI device specified by Target\r
760 and Lun.\r
761 @retval EFI_TIMEOUT A timeout occurred while attempting to reset\r
762 the SCSI device specified by Target and Lun.\r
763\r
764**/\r
765EFI_STATUS\r
766EFIAPI\r
767AtapiScsiPassThruResetTarget (\r
768 IN EFI_SCSI_PASS_THRU_PROTOCOL *This,\r
769 IN UINT32 Target,\r
770 IN UINT64 Lun\r
771 )\r
772{\r
773 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate;\r
774 UINT8 Command;\r
775 UINT8 DeviceSelect;\r
776\r
777 AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
778\r
779 if (Target > MAX_TARGET_ID) {\r
780 return EFI_INVALID_PARAMETER;\r
781 }\r
782 //\r
783 // Directly return EFI_SUCCESS if want to reset the host controller\r
784 //\r
785 if (Target == This->Mode->AdapterId) {\r
786 return EFI_SUCCESS;\r
787 }\r
788 \r
789 //\r
790 // According to Target ID, reset the Atapi I/O Register mapping\r
791 // (Target Id in [0,1] area, using gAtapiIoPortRegisters[0],\r
792 // Target Id in [2,3] area, using gAtapiIoPortRegisters[1]\r
793 //\r
794 if ((Target / 2) == 0) {\r
795 AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[0];\r
796 } else {\r
797 AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[1];\r
798 }\r
799 \r
800 //\r
801 // for ATAPI device, no need to wait DRDY ready after device selecting.\r
802 //\r
803 // bit7 and bit5 are both set to 1 for backward compatibility\r
804 //\r
805 DeviceSelect = (UINT8) (((bit (7) | bit (5)) | (Target << 4)));\r
806 WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect);\r
807\r
808 Command = ATAPI_SOFT_RESET_CMD;\r
809 WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command);\r
810\r
811 //\r
812 // BSY clear is the only status return to the host by the device\r
813 // when reset is complete.\r
814 // slave device needs at most 31s to clear BSY\r
815 //\r
816 if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000))) {\r
817 return EFI_DEVICE_ERROR;\r
818 }\r
819 \r
820 //\r
821 // stall 5 seconds to make the device status stable\r
822 //\r
823 gBS->Stall (5000000);\r
824\r
825 return EFI_SUCCESS;\r
826}\r
827\r
828 \r
829/**\r
830 Checks the parameters in the SCSI Request Packet to make sure\r
831 they are valid for a SCSI Pass Thru request.\r
832\r
833 @todo function comment is missing 'Routine Description:'\r
834 @todo function comment is missing 'Arguments:'\r
835 @todo function comment is missing 'Returns:'\r
836 @todo Packet - add argument and description to function comment\r
837 @todo EFI_INVALID_PARAMETER - add return value to function comment\r
838 @todo EFI_INVALID_PARAMETER - add return value to function comment\r
839 @todo EFI_INVALID_PARAMETER - add return value to function comment\r
840 @todo EFI_UNSUPPORTED - add return value to function comment\r
841 @todo EFI_SUCCESS - add return value to function comment\r
842**/\r
843EFI_STATUS\r
844CheckSCSIRequestPacket (\r
845 EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
846 )\r
847{\r
848 if (Packet == NULL) {\r
849 return EFI_INVALID_PARAMETER;\r
850 }\r
851\r
852 if (!ValidCdbLength (Packet->CdbLength)) {\r
853 return EFI_INVALID_PARAMETER;\r
854 }\r
855\r
856 if (Packet->Cdb == NULL) {\r
857 return EFI_INVALID_PARAMETER;\r
858 }\r
859 \r
860 //\r
861 // Checks whether the request command is supported.\r
862 //\r
863 if (!IsCommandValid (Packet)) {\r
864 return EFI_UNSUPPORTED;\r
865 }\r
866\r
867 return EFI_SUCCESS;\r
868}\r
869\r
870/**\r
871 Checks the requested SCSI command: \r
872 Is it supported by this driver?\r
873 Is the Data transfer direction reasonable?\r
874\r
875 @todo function comment is missing 'Routine Description:'\r
876 @todo function comment is missing 'Arguments:'\r
877 @todo function comment is missing 'Returns:'\r
878 @todo Packet - add argument and description to function comment\r
879**/\r
880BOOLEAN\r
881IsCommandValid (\r
882 EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
883 )\r
884{\r
885 UINT8 Index;\r
886 UINT8 *OpCode;\r
887\r
888 OpCode = (UINT8 *) (Packet->Cdb);\r
889\r
890 for (Index = 0; CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)); Index++) {\r
891\r
892 if (*OpCode == gSupportedATAPICommands[Index].OpCode) {\r
893 //\r
894 // Check whether the requested Command is supported by this driver\r
895 //\r
896 if (Packet->DataDirection == DataIn) {\r
897 //\r
898 // Check whether the requested data direction conforms to\r
899 // what it should be.\r
900 //\r
901 if (gSupportedATAPICommands[Index].Direction == DataOut) {\r
902 return FALSE;\r
903 }\r
904 }\r
905\r
906 if (Packet->DataDirection == DataOut) {\r
907 //\r
908 // Check whether the requested data direction conforms to\r
909 // what it should be.\r
910 //\r
911 if (gSupportedATAPICommands[Index].Direction == DataIn) {\r
912 return FALSE;\r
913 }\r
914 }\r
915\r
916 return TRUE;\r
917 }\r
918 }\r
919\r
920 return FALSE;\r
921}\r
922\r
923/**\r
924 Performs blocking I/O request.\r
925\r
926 @param AtapiScsiPrivate Private data structure for the specified channel.\r
927 @param Target The Target ID of the ATAPI device to send the SCSI\r
928 Request Packet. To ATAPI devices attached on an IDE\r
929 Channel, Target ID 0 indicates Master device;Target\r
930 ID 1 indicates Slave device.\r
931 @param Packet The SCSI Request Packet to send to the ATAPI device\r
932 specified by Target.\r
933\r
934 @todo AtapiScsiPrivate - add argument and description to function comment\r
935**/\r
936EFI_STATUS\r
937SubmitBlockingIoCommand (\r
938 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
939 UINT32 Target,\r
940 EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
941 )\r
942{\r
943 UINT8 PacketCommand[12];\r
944 UINT64 TimeoutInMicroSeconds;\r
945 EFI_STATUS PacketCommandStatus;\r
946\r
947 //\r
948 // Fill ATAPI Command Packet according to CDB\r
949 //\r
950 ZeroMem (&PacketCommand, 12);\r
951 CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength);\r
952\r
953 //\r
954 // Timeout is 100ns unit, convert it to 1000ns (1us) unit.\r
955 //\r
956 TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10);\r
957\r
958 //\r
959 // Submit ATAPI Command Packet\r
960 //\r
961 PacketCommandStatus = AtapiPacketCommand (\r
962 AtapiScsiPrivate,\r
963 Target,\r
964 PacketCommand,\r
965 Packet->DataBuffer,\r
966 &(Packet->TransferLength),\r
967 (DATA_DIRECTION) Packet->DataDirection,\r
968 TimeoutInMicroSeconds\r
969 );\r
970 if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) {\r
971 Packet->SenseDataLength = 0;\r
972 return PacketCommandStatus;\r
973 }\r
974 //\r
975 // Return SenseData if PacketCommandStatus matches\r
976 // the following return codes.\r
977 //\r
978 if ((PacketCommandStatus == EFI_WARN_BUFFER_TOO_SMALL) ||\r
979 (PacketCommandStatus == EFI_DEVICE_ERROR) ||\r
980 (PacketCommandStatus == EFI_TIMEOUT)) {\r
981 \r
982 //\r
983 // avoid submit request sense command continuously.\r
984 //\r
985 if (PacketCommand[0] == OP_REQUEST_SENSE) {\r
986 Packet->SenseDataLength = 0;\r
987 return PacketCommandStatus;\r
988 }\r
989\r
990 RequestSenseCommand (\r
991 AtapiScsiPrivate,\r
992 Target,\r
993 Packet->Timeout,\r
994 Packet->SenseData,\r
995 &Packet->SenseDataLength\r
996 );\r
997 }\r
998\r
999 return PacketCommandStatus;\r
1000}\r
1001\r
1002/**\r
1003 RequestSenseCommand\r
1004\r
1005 @param AtapiScsiPrivate\r
1006 @param Target\r
1007 @param Timeout\r
1008 @param SenseData\r
1009 @param SenseDataLength\r
1010\r
1011 @todo Add function description\r
1012 @todo AtapiScsiPrivate TODO: add argument description\r
1013 @todo Target TODO: add argument description\r
1014 @todo Timeout TODO: add argument description\r
1015 @todo SenseData TODO: add argument description\r
1016 @todo SenseDataLength TODO: add argument description\r
1017 @todo add return values\r
1018**/\r
1019EFI_STATUS\r
1020RequestSenseCommand (\r
1021 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1022 UINT32 Target,\r
1023 UINT64 Timeout,\r
1024 VOID *SenseData,\r
1025 UINT8 *SenseDataLength\r
1026 )\r
1027{\r
1028 EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET Packet;\r
1029 UINT8 Cdb[12];\r
1030 EFI_STATUS Status;\r
1031\r
1032 ZeroMem (&Packet, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET));\r
1033 ZeroMem (Cdb, 12);\r
1034\r
1035 Cdb[0] = OP_REQUEST_SENSE;\r
1036 Cdb[4] = (UINT8) (*SenseDataLength);\r
1037\r
1038 Packet.Timeout = Timeout;\r
1039 Packet.DataBuffer = SenseData;\r
1040 Packet.SenseData = NULL;\r
1041 Packet.Cdb = Cdb;\r
1042 Packet.TransferLength = *SenseDataLength;\r
1043 Packet.CdbLength = 12;\r
1044 Packet.DataDirection = DataIn;\r
1045\r
1046 Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, &Packet);\r
1047 *SenseDataLength = (UINT8) (Packet.TransferLength);\r
1048 return Status;\r
1049}\r
1050\r
1051/**\r
1052 Submits ATAPI command packet to the specified ATAPI device.\r
1053\r
1054 @param AtapiScsiPrivate: Private data structure for the specified channel.\r
1055 @param Target: The Target ID of the ATAPI device to send the SCSI\r
1056 Request Packet. To ATAPI devices attached on an IDE\r
1057 Channel, Target ID 0 indicates Master device;Target\r
1058 ID 1 indicates Slave device.\r
1059 @param PacketCommand: Points to the ATAPI command packet.\r
1060 @param Buffer: Points to the transferred data.\r
1061 @param ByteCount: When input,indicates the buffer size; when output,\r
1062 indicates the actually transferred data size.\r
1063 @param Direction: Indicates the data transfer direction.\r
1064 @param TimeoutInMicroSeconds: The timeout, in micro second units,\r
1065 to use for the execution of this ATAPI command.\r
1066 A TimeoutInMicroSeconds value of 0 means that\r
1067 this function will wait indefinitely for the ATAPI\r
1068 command to execute.\r
1069 <P>\r
1070 If TimeoutInMicroSeconds is greater than zero, then\r
1071 this function will return EFI_TIMEOUT if the time\r
1072 required to execute the ATAPI command is greater\r
1073 than TimeoutInMicroSeconds.\r
1074 </P>\r
1075\r
1076 @todo AtapiScsiPrivate - add argument and description to function comment\r
1077 @todo PacketCommand - add argument and description to function comment\r
1078 @todo Buffer - add argument and description to function comment\r
1079 @todo ByteCount - add argument and description to function comment\r
1080 @todo Direction - add argument and description to function comment\r
1081**/\r
1082EFI_STATUS\r
1083AtapiPacketCommand (\r
1084 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1085 UINT32 Target,\r
1086 UINT8 *PacketCommand,\r
1087 VOID *Buffer,\r
1088 UINT32 *ByteCount,\r
1089 DATA_DIRECTION Direction,\r
1090 UINT64 TimeoutInMicroSeconds\r
1091 )\r
1092{\r
1093\r
1094 UINT16 *CommandIndex;\r
1095 UINT8 Count;\r
1096 EFI_STATUS Status;\r
1097\r
1098 //\r
1099 // Set all the command parameters by fill related registers.\r
1100 // Before write to all the following registers, BSY and DRQ must be 0.\r
1101 //\r
1102 Status = StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
1103 if (EFI_ERROR (Status)) {\r
1104 if (Status == EFI_ABORTED) {\r
1105 Status = EFI_DEVICE_ERROR;\r
1106 }\r
1107\r
1108 *ByteCount = 0;\r
1109 return Status;\r
1110 }\r
1111 //\r
1112 // Select device via Device/Head Register.\r
1113 // "Target = 0" indicates device 0; "Target = 1" indicates device 1\r
1114 //\r
1115 WritePortB (\r
1116 AtapiScsiPrivate->PciIo,\r
1117 AtapiScsiPrivate->IoPort->Head,\r
1118 (UINT8) ((Target << 4) | DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000)\r
1119 );\r
1120\r
1121 //\r
1122 // No OVL; No DMA (by setting feature register)\r
1123 //\r
1124 WritePortB (\r
1125 AtapiScsiPrivate->PciIo,\r
1126 AtapiScsiPrivate->IoPort->Reg1.Feature,\r
1127 0x00\r
1128 );\r
1129\r
1130 //\r
1131 // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device\r
1132 // determine how much data should be transfered.\r
1133 //\r
1134 WritePortB (\r
1135 AtapiScsiPrivate->PciIo,\r
1136 AtapiScsiPrivate->IoPort->CylinderLsb,\r
1137 (UINT8) (MAX_ATAPI_BYTE_COUNT & 0x00ff)\r
1138 );\r
1139 WritePortB (\r
1140 AtapiScsiPrivate->PciIo,\r
1141 AtapiScsiPrivate->IoPort->CylinderMsb,\r
1142 (UINT8) (MAX_ATAPI_BYTE_COUNT >> 8)\r
1143 );\r
1144\r
1145 //\r
1146 // DEFAULT_CTL:0x0a (0000,1010)\r
1147 // Disable interrupt\r
1148 //\r
1149 WritePortB (\r
1150 AtapiScsiPrivate->PciIo,\r
1151 AtapiScsiPrivate->IoPort->Alt.DeviceControl,\r
1152 DEFAULT_CTL\r
1153 );\r
1154\r
1155 //\r
1156 // Send Packet command to inform device\r
1157 // that the following data bytes are command packet.\r
1158 //\r
1159 WritePortB (\r
1160 AtapiScsiPrivate->PciIo,\r
1161 AtapiScsiPrivate->IoPort->Reg.Command,\r
1162 PACKET_CMD\r
1163 );\r
1164\r
1165 //\r
1166 // Before data transfer, BSY should be 0 and DRQ should be 1.\r
1167 // if they are not in specified time frame,\r
1168 // retrieve Sense Key from Error Register before return.\r
1169 //\r
1170 Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
1171 if (EFI_ERROR (Status)) {\r
1172 if (Status == EFI_ABORTED) {\r
1173 Status = EFI_DEVICE_ERROR;\r
1174 }\r
1175\r
1176 *ByteCount = 0;\r
1177 return Status;\r
1178 }\r
1179\r
1180 //\r
1181 // Send out command packet\r
1182 //\r
1183 CommandIndex = (UINT16 *) PacketCommand;\r
1184 for (Count = 0; Count < 6; Count++, CommandIndex++) {\r
1185 WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *CommandIndex);\r
1186 }\r
1187\r
1188 //\r
1189 // call AtapiPassThruPioReadWriteData() function to get\r
1190 // requested transfer data form device.\r
1191 //\r
1192 return AtapiPassThruPioReadWriteData (\r
1193 AtapiScsiPrivate,\r
1194 Buffer,\r
1195 ByteCount,\r
1196 Direction,\r
1197 TimeoutInMicroSeconds\r
1198 );\r
1199}\r
1200\r
1201/**\r
1202 Performs data transfer between ATAPI device and host after the\r
1203 ATAPI command packet is sent.\r
1204\r
1205 @param AtapiScsiPrivate: Private data structure for the specified channel.\r
1206 @param Buffer: Points to the transferred data.\r
1207 @param ByteCount: When input,indicates the buffer size; when output,\r
1208 indicates the actually transferred data size.\r
1209 @param Direction: Indicates the data transfer direction.\r
1210 @param TimeoutInMicroSeconds: The timeout, in micro second units,\r
1211 to use for the execution of this ATAPI command.\r
1212 A TimeoutInMicroSeconds value of 0 means that\r
1213 this function will wait indefinitely for the ATAPI\r
1214 command to execute.\r
1215 <P>\r
1216 If TimeoutInMicroSeconds is greater than zero, then\r
1217 this function will return EFI_TIMEOUT if the time\r
1218 required to execute the ATAPI command is greater\r
1219 than TimeoutInMicroSeconds.\r
1220 </P>\r
1221\r
1222 @todo AtapiScsiPrivate - add argument and description to function comment\r
1223 @todo Buffer - add argument and description to function comment\r
1224 @todo ByteCount - add argument and description to function comment\r
1225 @todo Direction - add argument and description to function comment\r
1226 @todo EFI_DEVICE_ERROR - add return value to function comment\r
1227 @todo EFI_DEVICE_ERROR - add return value to function comment\r
1228 @todo EFI_WARN_BUFFER_TOO_SMALL - add return value to function comment\r
1229**/\r
1230EFI_STATUS\r
1231AtapiPassThruPioReadWriteData (\r
1232 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1233 UINT16 *Buffer,\r
1234 UINT32 *ByteCount,\r
1235 DATA_DIRECTION Direction,\r
1236 UINT64 TimeoutInMicroSeconds\r
1237 )\r
1238{\r
1239 UINT32 Index;\r
1240 UINT32 RequiredWordCount;\r
1241 UINT32 ActualWordCount;\r
1242\r
1243 UINT32 WordCount;\r
1244 EFI_STATUS Status;\r
1245 UINT16 *ptrBuffer;\r
1246\r
1247 Status = EFI_SUCCESS;\r
1248\r
1249 //\r
1250 // Non Data transfer request is also supported.\r
1251 //\r
1252 if (*ByteCount == 0 || Buffer == NULL) {\r
1253 *ByteCount = 0;\r
1254 if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds))) {\r
1255 return EFI_DEVICE_ERROR;\r
1256 }\r
1257 }\r
1258\r
1259 ptrBuffer = Buffer;\r
1260 RequiredWordCount = *ByteCount / 2;\r
1261\r
1262 //\r
1263 // ActuralWordCount means the word count of data really transfered.\r
1264 //\r
1265 ActualWordCount = 0;\r
1266\r
1267 while (ActualWordCount < RequiredWordCount) {\r
1268 //\r
1269 // before each data transfer stream, the host should poll DRQ bit ready,\r
1270 // which indicates device's ready for data transfer .\r
1271 //\r
1272 Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
1273 if (EFI_ERROR (Status)) {\r
1274 *ByteCount = ActualWordCount * 2;\r
1275\r
1276 AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);\r
1277\r
1278 if (ActualWordCount == 0) {\r
1279 return EFI_DEVICE_ERROR;\r
1280 }\r
1281 //\r
1282 // ActualWordCount > 0\r
1283 //\r
1284 if (ActualWordCount < RequiredWordCount) {\r
1285 return EFI_WARN_BUFFER_TOO_SMALL;\r
1286 }\r
1287 }\r
1288 //\r
1289 // get current data transfer size from Cylinder Registers.\r
1290 //\r
1291 WordCount = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderMsb) << 8;\r
1292 WordCount = WordCount | ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderLsb);\r
1293 WordCount = WordCount & 0xffff;\r
1294 WordCount /= 2;\r
1295\r
1296 //\r
1297 // perform a series data In/Out.\r
1298 //\r
1299 for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) {\r
1300\r
1301 if (Direction == DataIn) {\r
1302\r
1303 *ptrBuffer = ReadPortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data);\r
1304 } else {\r
1305\r
1306 WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *ptrBuffer);\r
1307 }\r
1308\r
1309 ptrBuffer++;\r
1310\r
1311 }\r
1312 }\r
1313 //\r
1314 // After data transfer is completed, normally, DRQ bit should clear.\r
1315 //\r
1316 StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
1317\r
1318 //\r
1319 // read status register to check whether error happens.\r
1320 //\r
1321 Status = AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);\r
1322\r
1323 *ByteCount = ActualWordCount * 2;\r
1324\r
1325 return Status;\r
1326}\r
1327\r
1328\r
1329/**\r
1330 Read one byte from a specified I/O port.\r
1331\r
1332 @todo function comment is missing 'Routine Description:'\r
1333 @todo function comment is missing 'Arguments:'\r
1334 @todo function comment is missing 'Returns:'\r
1335 @todo PciIo - add argument and description to function comment\r
1336 @todo Port - add argument and description to function comment\r
1337**/\r
1338UINT8\r
1339ReadPortB (\r
1340 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1341 IN UINT16 Port\r
1342 )\r
1343{\r
1344 UINT8 Data;\r
1345\r
1346 Data = 0;\r
1347 PciIo->Io.Read (\r
1348 PciIo,\r
1349 EfiPciIoWidthUint8,\r
1350 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1351 (UINT64) Port,\r
1352 1,\r
1353 &Data\r
1354 );\r
1355 return Data;\r
1356}\r
1357\r
1358\r
1359/**\r
1360 Read one word from a specified I/O port.\r
1361\r
1362 @todo function comment is missing 'Routine Description:'\r
1363 @todo function comment is missing 'Arguments:'\r
1364 @todo function comment is missing 'Returns:'\r
1365 @todo PciIo - add argument and description to function comment\r
1366 @todo Port - add argument and description to function comment\r
1367**/\r
1368UINT16\r
1369ReadPortW (\r
1370 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1371 IN UINT16 Port\r
1372 )\r
1373{\r
1374 UINT16 Data;\r
1375\r
1376 Data = 0;\r
1377 PciIo->Io.Read (\r
1378 PciIo,\r
1379 EfiPciIoWidthUint16,\r
1380 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1381 (UINT64) Port,\r
1382 1,\r
1383 &Data\r
1384 );\r
1385 return Data;\r
1386}\r
1387\r
1388\r
1389/**\r
1390 Write one byte to a specified I/O port.\r
1391\r
1392 @todo function comment is missing 'Routine Description:'\r
1393 @todo function comment is missing 'Arguments:'\r
1394 @todo function comment is missing 'Returns:'\r
1395 @todo PciIo - add argument and description to function comment\r
1396 @todo Port - add argument and description to function comment\r
1397 @todo Data - add argument and description to function comment\r
1398**/\r
1399VOID\r
1400WritePortB (\r
1401 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1402 IN UINT16 Port,\r
1403 IN UINT8 Data\r
1404 )\r
1405{\r
1406\r
1407 PciIo->Io.Write (\r
1408 PciIo,\r
1409 EfiPciIoWidthUint8,\r
1410 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1411 (UINT64) Port,\r
1412 1,\r
1413 &Data\r
1414 );\r
1415\r
1416}\r
1417\r
1418\r
1419/**\r
1420 Write one word to a specified I/O port.\r
1421\r
1422 @todo function comment is missing 'Routine Description:'\r
1423 @todo function comment is missing 'Arguments:'\r
1424 @todo function comment is missing 'Returns:'\r
1425 @todo PciIo - add argument and description to function comment\r
1426 @todo Port - add argument and description to function comment\r
1427 @todo Data - add argument and description to function comment\r
1428**/\r
1429VOID\r
1430WritePortW (\r
1431 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1432 IN UINT16 Port,\r
1433 IN UINT16 Data\r
1434 )\r
1435{\r
1436\r
1437 PciIo->Io.Write (\r
1438 PciIo,\r
1439 EfiPciIoWidthUint16,\r
1440 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1441 (UINT64) Port,\r
1442 1,\r
1443 &Data\r
1444 );\r
1445}\r
1446\r
1447/**\r
1448 Check whether DRQ is clear in the Status Register. (BSY must also be cleared)\r
1449 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1450 DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1451 elapsed.\r
1452\r
1453 @todo function comment is missing 'Routine Description:'\r
1454 @todo function comment is missing 'Arguments:'\r
1455 @todo function comment is missing 'Returns:'\r
1456 @todo AtapiScsiPrivate - add argument and description to function comment\r
1457 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1458 @todo EFI_ABORTED - add return value to function comment\r
1459 @todo EFI_TIMEOUT - add return value to function comment\r
1460 @todo EFI_SUCCESS - add return value to function comment\r
1461**/\r
1462EFI_STATUS\r
1463StatusDRQClear (\r
1464 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1465 UINT64 TimeoutInMicroSeconds\r
1466 )\r
1467{\r
1468 UINT64 Delay;\r
1469 UINT8 StatusRegister;\r
1470 UINT8 ErrRegister;\r
1471\r
1472 if (TimeoutInMicroSeconds == 0) {\r
1473 Delay = 2;\r
1474 } else {\r
1475 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1476 }\r
1477\r
1478 do {\r
1479\r
1480 StatusRegister = ReadPortB (\r
1481 AtapiScsiPrivate->PciIo,\r
1482 AtapiScsiPrivate->IoPort->Reg.Status\r
1483 );\r
1484\r
1485 //\r
1486 // wait for BSY == 0 and DRQ == 0\r
1487 //\r
1488 if ((StatusRegister & (DRQ | BSY)) == 0) {\r
1489 break;\r
1490 }\r
1491 //\r
1492 // check whether the command is aborted by the device\r
1493 //\r
1494 if ((StatusRegister & (BSY | ERR)) == ERR) {\r
1495\r
1496 ErrRegister = ReadPortB (\r
1497 AtapiScsiPrivate->PciIo,\r
1498 AtapiScsiPrivate->IoPort->Reg1.Error\r
1499 );\r
1500 if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
1501\r
1502 return EFI_ABORTED;\r
1503 }\r
1504 }\r
1505 //\r
1506 // Stall for 30 us\r
1507 //\r
1508 gBS->Stall (30);\r
1509\r
1510 //\r
1511 // Loop infinitely if not meeting expected condition\r
1512 //\r
1513 if (TimeoutInMicroSeconds == 0) {\r
1514 Delay = 2;\r
1515 }\r
1516\r
1517 Delay--;\r
1518 } while (Delay);\r
1519\r
1520 if (Delay == 0) {\r
1521 return EFI_TIMEOUT;\r
1522 }\r
1523\r
1524 return EFI_SUCCESS;\r
1525}\r
1526\r
1527/**\r
1528 Check whether DRQ is clear in the Alternate Status Register. \r
1529 (BSY must also be cleared).\r
1530 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1531 DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1532 elapsed.\r
1533\r
1534 @todo function comment is missing 'Routine Description:'\r
1535 @todo function comment is missing 'Arguments:'\r
1536 @todo function comment is missing 'Returns:'\r
1537 @todo AtapiScsiPrivate - add argument and description to function comment\r
1538 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1539 @todo EFI_ABORTED - add return value to function comment\r
1540 @todo EFI_TIMEOUT - add return value to function comment\r
1541 @todo EFI_SUCCESS - add return value to function comment\r
1542**/\r
1543EFI_STATUS\r
1544AltStatusDRQClear (\r
1545 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1546 UINT64 TimeoutInMicroSeconds\r
1547 )\r
1548{\r
1549 UINT64 Delay;\r
1550 UINT8 AltStatusRegister;\r
1551 UINT8 ErrRegister;\r
1552\r
1553 if (TimeoutInMicroSeconds == 0) {\r
1554 Delay = 2;\r
1555 } else {\r
1556 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1557 }\r
1558\r
1559 do {\r
1560\r
1561 AltStatusRegister = ReadPortB (\r
1562 AtapiScsiPrivate->PciIo,\r
1563 AtapiScsiPrivate->IoPort->Alt.AltStatus\r
1564 );\r
1565\r
1566 //\r
1567 // wait for BSY == 0 and DRQ == 0\r
1568 //\r
1569 if ((AltStatusRegister & (DRQ | BSY)) == 0) {\r
1570 break;\r
1571 }\r
1572\r
1573 if ((AltStatusRegister & (BSY | ERR)) == ERR) {\r
1574\r
1575 ErrRegister = ReadPortB (\r
1576 AtapiScsiPrivate->PciIo,\r
1577 AtapiScsiPrivate->IoPort->Reg1.Error\r
1578 );\r
1579 if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
1580\r
1581 return EFI_ABORTED;\r
1582 }\r
1583 }\r
1584 //\r
1585 // Stall for 30 us\r
1586 //\r
1587 gBS->Stall (30);\r
1588\r
1589 //\r
1590 // Loop infinitely if not meeting expected condition\r
1591 //\r
1592 if (TimeoutInMicroSeconds == 0) {\r
1593 Delay = 2;\r
1594 }\r
1595\r
1596 Delay--;\r
1597 } while (Delay);\r
1598\r
1599 if (Delay == 0) {\r
1600 return EFI_TIMEOUT;\r
1601 }\r
1602\r
1603 return EFI_SUCCESS;\r
1604}\r
1605\r
1606/**\r
1607 Check whether DRQ is ready in the Status Register. (BSY must also be cleared)\r
1608 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1609 DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1610 elapsed.\r
1611\r
1612 @todo function comment is missing 'Routine Description:'\r
1613 @todo function comment is missing 'Arguments:'\r
1614 @todo function comment is missing 'Returns:'\r
1615 @todo AtapiScsiPrivate - add argument and description to function comment\r
1616 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1617 @todo EFI_ABORTED - add return value to function comment\r
1618 @todo EFI_TIMEOUT - add return value to function comment\r
1619 @todo EFI_SUCCESS - add return value to function comment\r
1620**/\r
1621EFI_STATUS\r
1622StatusDRQReady (\r
1623 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1624 UINT64 TimeoutInMicroSeconds\r
1625 )\r
1626{\r
1627 UINT64 Delay;\r
1628 UINT8 StatusRegister;\r
1629 UINT8 ErrRegister;\r
1630\r
1631 if (TimeoutInMicroSeconds == 0) {\r
1632 Delay = 2;\r
1633 } else {\r
1634 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1635 }\r
1636\r
1637 do {\r
1638 //\r
1639 // read Status Register will clear interrupt\r
1640 //\r
1641 StatusRegister = ReadPortB (\r
1642 AtapiScsiPrivate->PciIo,\r
1643 AtapiScsiPrivate->IoPort->Reg.Status\r
1644 );\r
1645\r
1646 //\r
1647 // BSY==0,DRQ==1\r
1648 //\r
1649 if ((StatusRegister & (BSY | DRQ)) == DRQ) {\r
1650 break;\r
1651 }\r
1652\r
1653 if ((StatusRegister & (BSY | ERR)) == ERR) {\r
1654\r
1655 ErrRegister = ReadPortB (\r
1656 AtapiScsiPrivate->PciIo,\r
1657 AtapiScsiPrivate->IoPort->Reg1.Error\r
1658 );\r
1659 if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
1660 return EFI_ABORTED;\r
1661 }\r
1662 }\r
1663\r
1664 //\r
1665 // Stall for 30 us\r
1666 //\r
1667 gBS->Stall (30);\r
1668\r
1669 //\r
1670 // Loop infinitely if not meeting expected condition\r
1671 //\r
1672 if (TimeoutInMicroSeconds == 0) {\r
1673 Delay = 2;\r
1674 }\r
1675\r
1676 Delay--;\r
1677 } while (Delay);\r
1678\r
1679 if (Delay == 0) {\r
1680 return EFI_TIMEOUT;\r
1681 }\r
1682\r
1683 return EFI_SUCCESS;\r
1684}\r
1685\r
1686/**\r
1687 Check whether DRQ is ready in the Alternate Status Register. \r
1688 (BSY must also be cleared)\r
1689 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1690 DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1691 elapsed.\r
1692\r
1693 @todo function comment is missing 'Routine Description:'\r
1694 @todo function comment is missing 'Arguments:'\r
1695 @todo function comment is missing 'Returns:'\r
1696 @todo AtapiScsiPrivate - add argument and description to function comment\r
1697 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1698 @todo EFI_ABORTED - add return value to function comment\r
1699 @todo EFI_TIMEOUT - add return value to function comment\r
1700 @todo EFI_SUCCESS - add return value to function comment\r
1701**/\r
1702EFI_STATUS\r
1703AltStatusDRQReady (\r
1704 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1705 UINT64 TimeoutInMicroSeconds\r
1706 )\r
1707{\r
1708 UINT64 Delay;\r
1709 UINT8 AltStatusRegister;\r
1710 UINT8 ErrRegister;\r
1711\r
1712 if (TimeoutInMicroSeconds == 0) {\r
1713 Delay = 2;\r
1714 } else {\r
1715 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1716 }\r
1717\r
1718 do {\r
1719 //\r
1720 // read Status Register will clear interrupt\r
1721 //\r
1722 AltStatusRegister = ReadPortB (\r
1723 AtapiScsiPrivate->PciIo,\r
1724 AtapiScsiPrivate->IoPort->Alt.AltStatus\r
1725 );\r
1726 //\r
1727 // BSY==0,DRQ==1\r
1728 //\r
1729 if ((AltStatusRegister & (BSY | DRQ)) == DRQ) {\r
1730 break;\r
1731 }\r
1732\r
1733 if ((AltStatusRegister & (BSY | ERR)) == ERR) {\r
1734\r
1735 ErrRegister = ReadPortB (\r
1736 AtapiScsiPrivate->PciIo,\r
1737 AtapiScsiPrivate->IoPort->Reg1.Error\r
1738 );\r
1739 if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
1740 return EFI_ABORTED;\r
1741 }\r
1742 }\r
1743\r
1744 //\r
1745 // Stall for 30 us\r
1746 //\r
1747 gBS->Stall (30);\r
1748\r
1749 //\r
1750 // Loop infinitely if not meeting expected condition\r
1751 //\r
1752 if (TimeoutInMicroSeconds == 0) {\r
1753 Delay = 2;\r
1754 }\r
1755\r
1756 Delay--;\r
1757 } while (Delay);\r
1758\r
1759 if (Delay == 0) {\r
1760 return EFI_TIMEOUT;\r
1761 }\r
1762\r
1763 return EFI_SUCCESS;\r
1764}\r
1765\r
1766/**\r
1767 Check whether BSY is clear in the Status Register.\r
1768 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1769 BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1770 elapsed.\r
1771\r
1772 @todo function comment is missing 'Routine Description:'\r
1773 @todo function comment is missing 'Arguments:'\r
1774 @todo function comment is missing 'Returns:'\r
1775 @todo AtapiScsiPrivate - add argument and description to function comment\r
1776 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1777 @todo EFI_TIMEOUT - add return value to function comment\r
1778 @todo EFI_SUCCESS - add return value to function comment\r
1779**/\r
1780EFI_STATUS\r
1781StatusWaitForBSYClear (\r
1782 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1783 UINT64 TimeoutInMicroSeconds\r
1784 )\r
1785{\r
1786 UINT64 Delay;\r
1787 UINT8 StatusRegister;\r
1788\r
1789 if (TimeoutInMicroSeconds == 0) {\r
1790 Delay = 2;\r
1791 } else {\r
1792 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1793 }\r
1794\r
1795 do {\r
1796\r
1797 StatusRegister = ReadPortB (\r
1798 AtapiScsiPrivate->PciIo,\r
1799 AtapiScsiPrivate->IoPort->Reg.Status\r
1800 );\r
1801 if ((StatusRegister & BSY) == 0x00) {\r
1802 break;\r
1803 }\r
1804\r
1805 //\r
1806 // Stall for 30 us\r
1807 //\r
1808 gBS->Stall (30);\r
1809\r
1810 //\r
1811 // Loop infinitely if not meeting expected condition\r
1812 //\r
1813 if (TimeoutInMicroSeconds == 0) {\r
1814 Delay = 2;\r
1815 }\r
1816\r
1817 Delay--;\r
1818 } while (Delay);\r
1819\r
1820 if (Delay == 0) {\r
1821 return EFI_TIMEOUT;\r
1822 }\r
1823\r
1824 return EFI_SUCCESS;\r
1825}\r
1826\r
1827/**\r
1828 Check whether BSY is clear in the Alternate Status Register.\r
1829 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1830 BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1831 elapsed.\r
1832\r
1833 @todo function comment is missing 'Routine Description:'\r
1834 @todo function comment is missing 'Arguments:'\r
1835 @todo function comment is missing 'Returns:'\r
1836 @todo AtapiScsiPrivate - add argument and description to function comment\r
1837 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1838 @todo EFI_TIMEOUT - add return value to function comment\r
1839 @todo EFI_SUCCESS - add return value to function comment\r
1840**/\r
1841EFI_STATUS\r
1842AltStatusWaitForBSYClear (\r
1843 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1844 UINT64 TimeoutInMicroSeconds\r
1845 )\r
1846{\r
1847 UINT64 Delay;\r
1848 UINT8 AltStatusRegister;\r
1849\r
1850 if (TimeoutInMicroSeconds == 0) {\r
1851 Delay = 2;\r
1852 } else {\r
1853 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1854 }\r
1855\r
1856 do {\r
1857\r
1858 AltStatusRegister = ReadPortB (\r
1859 AtapiScsiPrivate->PciIo,\r
1860 AtapiScsiPrivate->IoPort->Alt.AltStatus\r
1861 );\r
1862 if ((AltStatusRegister & BSY) == 0x00) {\r
1863 break;\r
1864 }\r
1865\r
1866 //\r
1867 // Stall for 30 us\r
1868 //\r
1869 gBS->Stall (30);\r
1870 //\r
1871 // Loop infinitely if not meeting expected condition\r
1872 //\r
1873 if (TimeoutInMicroSeconds == 0) {\r
1874 Delay = 2;\r
1875 }\r
1876\r
1877 Delay--;\r
1878 } while (Delay);\r
1879\r
1880 if (Delay == 0) {\r
1881 return EFI_TIMEOUT;\r
1882 }\r
1883\r
1884 return EFI_SUCCESS;\r
1885}\r
1886\r
1887/**\r
1888 Check whether DRDY is ready in the Status Register. \r
1889 (BSY must also be cleared)\r
1890 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1891 DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1892 elapsed.\r
1893\r
1894 @todo function comment is missing 'Routine Description:'\r
1895 @todo function comment is missing 'Arguments:'\r
1896 @todo function comment is missing 'Returns:'\r
1897 @todo AtapiScsiPrivate - add argument and description to function comment\r
1898 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1899 @todo EFI_ABORTED - add return value to function comment\r
1900 @todo EFI_TIMEOUT - add return value to function comment\r
1901 @todo EFI_SUCCESS - add return value to function comment\r
1902**/\r
1903EFI_STATUS\r
1904StatusDRDYReady (\r
1905 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1906 UINT64 TimeoutInMicroSeconds\r
1907 )\r
1908{\r
1909 UINT64 Delay;\r
1910 UINT8 StatusRegister;\r
1911 UINT8 ErrRegister;\r
1912\r
1913 if (TimeoutInMicroSeconds == 0) {\r
1914 Delay = 2;\r
1915 } else {\r
1916 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1917 }\r
1918\r
1919 do {\r
1920 StatusRegister = ReadPortB (\r
1921 AtapiScsiPrivate->PciIo,\r
1922 AtapiScsiPrivate->IoPort->Reg.Status\r
1923 );\r
1924 //\r
1925 // BSY == 0 , DRDY == 1\r
1926 //\r
1927 if ((StatusRegister & (DRDY | BSY)) == DRDY) {\r
1928 break;\r
1929 }\r
1930\r
1931 if ((StatusRegister & (BSY | ERR)) == ERR) {\r
1932\r
1933 ErrRegister = ReadPortB (\r
1934 AtapiScsiPrivate->PciIo,\r
1935 AtapiScsiPrivate->IoPort->Reg1.Error\r
1936 );\r
1937 if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
1938 return EFI_ABORTED;\r
1939 }\r
1940 }\r
1941 \r
1942 //\r
1943 // Stall for 30 us\r
1944 //\r
1945 gBS->Stall (30);\r
1946 //\r
1947 // Loop infinitely if not meeting expected condition\r
1948 //\r
1949 if (TimeoutInMicroSeconds == 0) {\r
1950 Delay = 2;\r
1951 }\r
1952\r
1953 Delay--;\r
1954 } while (Delay);\r
1955\r
1956 if (Delay == 0) {\r
1957 return EFI_TIMEOUT;\r
1958 }\r
1959\r
1960 return EFI_SUCCESS;\r
1961}\r
1962\r
1963/**\r
1964 Check whether DRDY is ready in the Alternate Status Register. \r
1965 (BSY must also be cleared)\r
1966 If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
1967 DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
1968 elapsed.\r
1969\r
1970 @todo function comment is missing 'Routine Description:'\r
1971 @todo function comment is missing 'Arguments:'\r
1972 @todo function comment is missing 'Returns:'\r
1973 @todo AtapiScsiPrivate - add argument and description to function comment\r
1974 @todo TimeoutInMicroSeconds - add argument and description to function comment\r
1975 @todo EFI_ABORTED - add return value to function comment\r
1976 @todo EFI_TIMEOUT - add return value to function comment\r
1977 @todo EFI_SUCCESS - add return value to function comment\r
1978**/\r
1979EFI_STATUS\r
1980AltStatusDRDYReady (\r
1981 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,\r
1982 UINT64 TimeoutInMicroSeconds\r
1983 )\r
1984{\r
1985 UINT64 Delay;\r
1986 UINT8 AltStatusRegister;\r
1987 UINT8 ErrRegister;\r
1988\r
1989 if (TimeoutInMicroSeconds == 0) {\r
1990 Delay = 2;\r
1991 } else {\r
1992 Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
1993 }\r
1994\r
1995 do {\r
1996 AltStatusRegister = ReadPortB (\r
1997 AtapiScsiPrivate->PciIo,\r
1998 AtapiScsiPrivate->IoPort->Alt.AltStatus\r
1999 );\r
2000 //\r
2001 // BSY == 0 , DRDY == 1\r
2002 //\r
2003 if ((AltStatusRegister & (DRDY | BSY)) == DRDY) {\r
2004 break;\r
2005 }\r
2006\r
2007 if ((AltStatusRegister & (BSY | ERR)) == ERR) {\r
2008\r
2009 ErrRegister = ReadPortB (\r
2010 AtapiScsiPrivate->PciIo,\r
2011 AtapiScsiPrivate->IoPort->Reg1.Error\r
2012 );\r
2013 if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
2014 return EFI_ABORTED;\r
2015 }\r
2016 }\r
2017\r
2018 //\r
2019 // Stall for 30 us\r
2020 //\r
2021 gBS->Stall (30);\r
2022 //\r
2023 // Loop infinitely if not meeting expected condition\r
2024 //\r
2025 if (TimeoutInMicroSeconds == 0) {\r
2026 Delay = 2;\r
2027 }\r
2028\r
2029 Delay--;\r
2030 } while (Delay);\r
2031\r
2032 if (Delay == 0) {\r
2033 return EFI_TIMEOUT;\r
2034 }\r
2035\r
2036 return EFI_SUCCESS;\r
2037}\r
2038\r
2039/**\r
2040 Check Error Register for Error Information. \r
2041\r
2042 @todo function comment is missing 'Routine Description:'\r
2043 @todo function comment is missing 'Arguments:'\r
2044 @todo function comment is missing 'Returns:'\r
2045 @todo AtapiScsiPrivate - add argument and description to function comment\r
2046 @todo EFI_SUCCESS - add return value to function comment\r
2047 @todo EFI_DEVICE_ERROR - add return value to function comment\r
2048**/\r
2049EFI_STATUS\r
2050AtapiPassThruCheckErrorStatus (\r
2051 ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate\r
2052 )\r
2053{\r
2054 UINT8 StatusRegister;\r
2055 UINT8 ErrorRegister;\r
2056\r
2057 StatusRegister = ReadPortB (\r
2058 AtapiScsiPrivate->PciIo,\r
2059 AtapiScsiPrivate->IoPort->Reg.Status\r
2060 );\r
2061 \r
2062 DEBUG_CODE_BEGIN ();\r
2063\r
2064 if (StatusRegister & DWF) {\r
2065 DEBUG (\r
2066 (EFI_D_BLKIO,\r
2067 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Write Fault\n",\r
2068 StatusRegister)\r
2069 );\r
2070 }\r
2071\r
2072 if (StatusRegister & CORR) {\r
2073 DEBUG (\r
2074 (EFI_D_BLKIO,\r
2075 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Corrected Data\n",\r
2076 StatusRegister)\r
2077 );\r
2078 }\r
2079\r
2080 if (StatusRegister & ERR) {\r
2081 ErrorRegister = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg1.Error);\r
2082 \r
2083\r
2084 if (ErrorRegister & BBK_ERR) {\r
2085 DEBUG (\r
2086 (EFI_D_BLKIO,\r
2087 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Bad Block Detected\n",\r
2088 ErrorRegister)\r
2089 );\r
2090 }\r
2091\r
2092 if (ErrorRegister & UNC_ERR) {\r
2093 DEBUG (\r
2094 (EFI_D_BLKIO,\r
2095 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Uncorrectable Data\n",\r
2096 ErrorRegister)\r
2097 );\r
2098 }\r
2099\r
2100 if (ErrorRegister & MC_ERR) {\r
2101 DEBUG (\r
2102 (EFI_D_BLKIO,\r
2103 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Media Change\n",\r
2104 ErrorRegister)\r
2105 );\r
2106 }\r
2107\r
2108 if (ErrorRegister & ABRT_ERR) {\r
2109 DEBUG (\r
2110 (EFI_D_BLKIO,\r
2111 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Abort\n",\r
2112 ErrorRegister)\r
2113 );\r
2114 }\r
2115\r
2116 if (ErrorRegister & TK0NF_ERR) {\r
2117 DEBUG (\r
2118 (EFI_D_BLKIO,\r
2119 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Track 0 Not Found\n",\r
2120 ErrorRegister)\r
2121 );\r
2122 }\r
2123\r
2124 if (ErrorRegister & AMNF_ERR) {\r
2125 DEBUG (\r
2126 (EFI_D_BLKIO,\r
2127 "AtapiPassThruCheckErrorStatus()-- %02x : Error : Address Mark Not Found\n",\r
2128 ErrorRegister)\r
2129 );\r
2130 }\r
2131 }\r
2132\r
2133 DEBUG_CODE_END ();\r
2134\r
2135 if ((StatusRegister & (ERR | DWF | CORR)) == 0) {\r
2136 return EFI_SUCCESS;\r
2137 }\r
2138\r
2139 \r
2140 return EFI_DEVICE_ERROR;\r
2141}\r
2142\r
2143/**\r
2144 The user Entry Point for module AtapiPassThru. The user code starts with this function.\r
2145\r
2146 @param[in] ImageHandle The firmware allocated handle for the EFI image. \r
2147 @param[in] SystemTable A pointer to the EFI System Table.\r
2148 \r
2149 @retval EFI_SUCCESS The entry point is executed successfully.\r
2150 @retval other Some error occurs when executing this entry point.\r
2151\r
2152**/\r
2153EFI_STATUS\r
2154EFIAPI\r
2155InitializeAtapiPassThru(\r
2156 IN EFI_HANDLE ImageHandle,\r
2157 IN EFI_SYSTEM_TABLE *SystemTable\r
2158 )\r
2159{\r
2160 EFI_STATUS Status;\r
2161\r
2162 //\r
2163 // Install driver model protocol(s).\r
2164 //\r
2165 Status = EfiLibInstallAllDriverProtocols (\r
2166 ImageHandle,\r
2167 SystemTable,\r
2168 &gAtapiScsiPassThruDriverBinding,\r
2169 ImageHandle,\r
2170 &gAtapiScsiPassThruComponentName,\r
2171 NULL,\r
2172 NULL\r
2173 );\r
2174 ASSERT_EFI_ERROR (Status);\r
2175\r
2176 return Status;\r
2177}\r