1/** @file
2Pei USB ATATPI command implementations.
3
4Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.<BR>
5
6This program and the accompanying materials
7are licensed and made available under the terms and conditions
8of the BSD License which accompanies this distribution.  The
9full text of the license may be found at
10http://opensource.org/licenses/bsd-license.php
11
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15**/
16
17#include "UsbBotPeim.h"
18#include "BotPeim.h"
19
20#define MAXSENSEKEY 5
21
22/**
23  Sends out ATAPI Inquiry Packet Command to the specified device. This command will
24  return INQUIRY data of the device.
25
26  @param PeiServices    The pointer of EFI_PEI_SERVICES.
27  @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
28
29  @retval EFI_SUCCESS       Inquiry command completes successfully.
30  @retval EFI_DEVICE_ERROR  Inquiry command failed.
31
32**/
33EFI_STATUS
34PeiUsbInquiry (
35  IN  EFI_PEI_SERVICES  **PeiServices,
36  IN  PEI_BOT_DEVICE    *PeiBotDevice
37  )
38{
39  ATAPI_PACKET_COMMAND  Packet;
40  EFI_STATUS            Status;
41  ATAPI_INQUIRY_DATA          Idata;
42
43  //
44  // fill command packet
45  //
46  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
47  ZeroMem (&Idata, sizeof (ATAPI_INQUIRY_DATA));
48
49  Packet.Inquiry.opcode             = ATA_CMD_INQUIRY;
50  Packet.Inquiry.page_code          = 0;
51  Packet.Inquiry.allocation_length  = 36;
52
53  //
54  // Send scsi INQUIRY command packet.
55  // According to SCSI Primary Commands-2 spec, host only needs to
56  // retrieve the first 36 bytes for standard INQUIRY data.
57  //
58  Status = PeiAtapiCommand (
59            PeiServices,
60            PeiBotDevice,
61            &Packet,
62            (UINT8) sizeof (ATAPI_PACKET_COMMAND),
63            &Idata,
64            36,
65            EfiUsbDataIn,
66            2000
67            );
68
69  if (EFI_ERROR (Status)) {
70    return EFI_DEVICE_ERROR;
71  }
72
73  if ((Idata.peripheral_type & 0x1f) == 0x05) {
74    PeiBotDevice->DeviceType      = USBCDROM;
75    PeiBotDevice->Media.BlockSize = 0x800;
76    PeiBotDevice->Media2.ReadOnly       = TRUE;
77    PeiBotDevice->Media2.RemovableMedia = TRUE;
78    PeiBotDevice->Media2.BlockSize      = 0x800;
79  } else {
80    PeiBotDevice->DeviceType      = USBFLOPPY;
81    PeiBotDevice->Media.BlockSize = 0x200;
82    PeiBotDevice->Media2.ReadOnly       = FALSE;
83    PeiBotDevice->Media2.RemovableMedia = TRUE;
84    PeiBotDevice->Media2.BlockSize      = 0x200;
85  }
86
87  return EFI_SUCCESS;
88}
89
90/**
91  Sends out ATAPI Test Unit Ready Packet Command to the specified device
92  to find out whether device is accessible.
93
94  @param PeiServices    The pointer of EFI_PEI_SERVICES.
95  @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
96
97  @retval EFI_SUCCESS        TestUnit command executed successfully.
98  @retval EFI_DEVICE_ERROR   Device cannot be executed TestUnit command successfully.
99
100**/
101EFI_STATUS
102PeiUsbTestUnitReady (
103  IN  EFI_PEI_SERVICES  **PeiServices,
104  IN  PEI_BOT_DEVICE    *PeiBotDevice
105  )
106{
107  ATAPI_PACKET_COMMAND  Packet;
108  EFI_STATUS            Status;
109
110  //
111  // fill command packet
112  //
113  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
114  Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY;
115
116  //
117  // send command packet
118  //
119  Status = PeiAtapiCommand (
120            PeiServices,
121            PeiBotDevice,
122            &Packet,
123            (UINT8) sizeof (ATAPI_PACKET_COMMAND),
124            NULL,
125            0,
126            EfiUsbNoData,
127            2000
128            );
129
130  if (EFI_ERROR (Status)) {
131    return EFI_DEVICE_ERROR;
132  }
133
134  return EFI_SUCCESS;
135}
136
137/**
138  Sends out ATAPI Request Sense Packet Command to the specified device.
139
140  @param PeiServices    The pointer of EFI_PEI_SERVICES.
141  @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
142  @param SenseCounts    Length of sense buffer.
143  @param SenseKeyBuffer Pointer to sense buffer.
144
145  @retval EFI_SUCCESS           Command executed successfully.
146  @retval EFI_DEVICE_ERROR      Some device errors happen.
147
148**/
149EFI_STATUS
150PeiUsbRequestSense (
151  IN  EFI_PEI_SERVICES  **PeiServices,
152  IN  PEI_BOT_DEVICE    *PeiBotDevice,
153  OUT UINTN             *SenseCounts,
154  IN  UINT8             *SenseKeyBuffer
155  )
156{
157  EFI_STATUS                  Status;
158  ATAPI_PACKET_COMMAND        Packet;
159  UINT8                       *Ptr;
160  BOOLEAN                     SenseReq;
161  ATAPI_REQUEST_SENSE_DATA    *Sense;
162
163  *SenseCounts = 0;
164
165  //
166  // fill command packet for Request Sense Packet Command
167  //
168  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
169  Packet.RequestSence.opcode            = ATA_CMD_REQUEST_SENSE;
170  Packet.RequestSence.allocation_length = (UINT8) sizeof (ATAPI_REQUEST_SENSE_DATA);
171
172  Ptr = SenseKeyBuffer;
173
174  SenseReq = TRUE;
175
176  //
177  //  request sense data from device continuously
178  //  until no sense data exists in the device.
179  //
180  while (SenseReq) {
181    Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr;
182
183    //
184    // send out Request Sense Packet Command and get one Sense
185    // data form device.
186    //
187    Status = PeiAtapiCommand (
188              PeiServices,
189              PeiBotDevice,
190              &Packet,
191              (UINT8) sizeof (ATAPI_PACKET_COMMAND),
192              (VOID *) Ptr,
193              sizeof (ATAPI_REQUEST_SENSE_DATA),
194              EfiUsbDataIn,
195              2000
196              );
197
198    //
199    // failed to get Sense data
200    //
201    if (EFI_ERROR (Status)) {
202      if (*SenseCounts == 0) {
203        return EFI_DEVICE_ERROR;
204      } else {
205        return EFI_SUCCESS;
206      }
207    }
208
209    if (Sense->sense_key != ATA_SK_NO_SENSE) {
210
211      Ptr += sizeof (ATAPI_REQUEST_SENSE_DATA);
212      //
213      // Ptr is byte based pointer
214      //
215      (*SenseCounts)++;
216
217      if (*SenseCounts == MAXSENSEKEY) {
218        break;
219      }
220
221    } else {
222      //
223      // when no sense key, skip out the loop
224      //
225      SenseReq = FALSE;
226    }
227  }
228
229  return EFI_SUCCESS;
230}
231
232/**
233  Sends out ATAPI Read Capacity Packet Command to the specified device.
234  This command will return the information regarding the capacity of the
235  media in the device.
236
237  @param PeiServices    The pointer of EFI_PEI_SERVICES.
238  @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
239
240  @retval EFI_SUCCESS           Command executed successfully.
241  @retval EFI_DEVICE_ERROR      Some device errors happen.
242
243**/
244EFI_STATUS
245PeiUsbReadCapacity (
246  IN  EFI_PEI_SERVICES  **PeiServices,
247  IN  PEI_BOT_DEVICE    *PeiBotDevice
248  )
249{
250  EFI_STATUS                  Status;
251  ATAPI_PACKET_COMMAND        Packet;
252  ATAPI_READ_CAPACITY_DATA    Data;
253  UINT32                      LastBlock;
254
255  ZeroMem (&Data, sizeof (ATAPI_READ_CAPACITY_DATA));
256  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
257
258  Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY;
259
260  //
261  // send command packet
262  //
263  Status = PeiAtapiCommand (
264            PeiServices,
265            PeiBotDevice,
266            &Packet,
267            (UINT8) sizeof (ATAPI_PACKET_COMMAND),
268            (VOID *) &Data,
269            sizeof (ATAPI_READ_CAPACITY_DATA),
270            EfiUsbDataIn,
271            2000
272            );
273
274  if (EFI_ERROR (Status)) {
275    return EFI_DEVICE_ERROR;
276  }
277  LastBlock = (Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0;
278
279  if (LastBlock == 0xFFFFFFFF) {
280    DEBUG ((EFI_D_INFO, "The usb device LBA count is larger than 0xFFFFFFFF!\n"));
281  }
282
283  PeiBotDevice->Media.LastBlock    = LastBlock;
284  PeiBotDevice->Media.MediaPresent = TRUE;
285
286  PeiBotDevice->Media2.LastBlock    = LastBlock;
287  PeiBotDevice->Media2.MediaPresent = TRUE;
288
289  return EFI_SUCCESS;
290}
291
292/**
293  Sends out ATAPI Read Format Capacity Data Command to the specified device.
294  This command will return the information regarding the capacity of the
295  media in the device.
296
297  @param PeiServices    The pointer of EFI_PEI_SERVICES.
298  @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
299
300  @retval EFI_SUCCESS           Command executed successfully.
301  @retval EFI_DEVICE_ERROR      Some device errors happen.
302
303**/
304EFI_STATUS
305PeiUsbReadFormattedCapacity (
306  IN  EFI_PEI_SERVICES  **PeiServices,
307  IN  PEI_BOT_DEVICE    *PeiBotDevice
308  )
309{
310  EFI_STATUS                      Status;
311  ATAPI_PACKET_COMMAND            Packet;
312  ATAPI_READ_FORMAT_CAPACITY_DATA FormatData;
313  UINT32                          LastBlock;
314
315  ZeroMem (&FormatData, sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA));
316  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
317
318  Packet.ReadFormatCapacity.opcode                = ATA_CMD_READ_FORMAT_CAPACITY;
319  Packet.ReadFormatCapacity.allocation_length_lo  = 12;
320
321  //
322  // send command packet
323  //
324  Status = PeiAtapiCommand (
325            PeiServices,
326            PeiBotDevice,
327            &Packet,
328            (UINT8) sizeof (ATAPI_PACKET_COMMAND),
329            (VOID *) &FormatData,
330            sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA),
331            EfiUsbDataIn,
332            2000
333            );
334
335  if (EFI_ERROR (Status)) {
336    return EFI_DEVICE_ERROR;
337  }
338
339  if (FormatData.DesCode == 3) {
340    //
341    // Media is not present
342    //
343    PeiBotDevice->Media.MediaPresent  = FALSE;
344    PeiBotDevice->Media.LastBlock     = 0;
345    PeiBotDevice->Media2.MediaPresent  = FALSE;
346    PeiBotDevice->Media2.LastBlock     = 0;
347
348  } else {
349    LastBlock = (FormatData.LastLba3 << 24) | (FormatData.LastLba2 << 16) | (FormatData.LastLba1 << 8) | FormatData.LastLba0;
350    if (LastBlock == 0xFFFFFFFF) {
351      DEBUG ((EFI_D_INFO, "The usb device LBA count is larger than 0xFFFFFFFF!\n"));
352    }
353
354    PeiBotDevice->Media.LastBlock = LastBlock;
355
356    PeiBotDevice->Media.LastBlock--;
357
358    PeiBotDevice->Media.MediaPresent = TRUE;
359
360    PeiBotDevice->Media2.MediaPresent = TRUE;
361    PeiBotDevice->Media2.LastBlock    = PeiBotDevice->Media.LastBlock;
362  }
363
364  return EFI_SUCCESS;
365}
366
367/**
368  Execute Read(10) ATAPI command on a specific SCSI target.
369
370  Executes the ATAPI Read(10) command on the ATAPI target specified by PeiBotDevice.
371
372  @param PeiServices       The pointer of EFI_PEI_SERVICES.
373  @param PeiBotDevice      The pointer to PEI_BOT_DEVICE instance.
374  @param Buffer            The pointer to data buffer.
375  @param Lba               The start logic block address of reading.
376  @param NumberOfBlocks    The block number of reading.
377
378  @retval EFI_SUCCESS           Command executed successfully.
379  @retval EFI_DEVICE_ERROR      Some device errors happen.
380
381**/
382EFI_STATUS
383PeiUsbRead10 (
384  IN  EFI_PEI_SERVICES  **PeiServices,
385  IN  PEI_BOT_DEVICE    *PeiBotDevice,
386  IN  VOID              *Buffer,
387  IN  EFI_PEI_LBA       Lba,
388  IN  UINTN             NumberOfBlocks
389  )
390{
391  ATAPI_PACKET_COMMAND  Packet;
392  ATAPI_READ10_CMD      *Read10Packet;
393  UINT16                MaxBlock;
394  UINT16                BlocksRemaining;
395  UINT16                SectorCount;
396  UINT32                Lba32;
397  UINT32                BlockSize;
398  UINT32                ByteCount;
399  VOID                  *PtrBuffer;
400  EFI_STATUS            Status;
401  UINT16                TimeOut;
402
403  //
404  // prepare command packet for the Inquiry Packet Command.
405  //
406  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
407  Read10Packet    = &Packet.Read10;
408  Lba32           = (UINT32) Lba;
409  PtrBuffer       = Buffer;
410
411  BlockSize       = (UINT32) PeiBotDevice->Media.BlockSize;
412
413  MaxBlock        = (UINT16) (65535 / BlockSize);
414  BlocksRemaining = (UINT16) NumberOfBlocks;
415
416  Status          = EFI_SUCCESS;
417  while (BlocksRemaining > 0) {
418
419    if (BlocksRemaining <= MaxBlock) {
420
421      SectorCount = BlocksRemaining;
422
423    } else {
424
425      SectorCount = MaxBlock;
426    }
427    //
428    // fill the Packet data structure
429    //
430    Read10Packet->opcode = ATA_CMD_READ_10;
431
432    //
433    // Lba0 ~ Lba3 specify the start logical block address of the data transfer.
434    // Lba0 is MSB, Lba3 is LSB
435    //
436    Read10Packet->Lba3  = (UINT8) (Lba32 & 0xff);
437    Read10Packet->Lba2  = (UINT8) (Lba32 >> 8);
438    Read10Packet->Lba1  = (UINT8) (Lba32 >> 16);
439    Read10Packet->Lba0  = (UINT8) (Lba32 >> 24);
440
441    //
442    // TranLen0 ~ TranLen1 specify the transfer length in block unit.
443    // TranLen0 is MSB, TranLen is LSB
444    //
445    Read10Packet->TranLen1  = (UINT8) (SectorCount & 0xff);
446    Read10Packet->TranLen0  = (UINT8) (SectorCount >> 8);
447
448    ByteCount               = SectorCount * BlockSize;
449
450    TimeOut                 = (UINT16) (SectorCount * 2000);
451
452    //
453    // send command packet
454    //
455    Status = PeiAtapiCommand (
456              PeiServices,
457              PeiBotDevice,
458              &Packet,
459              (UINT8) sizeof (ATAPI_PACKET_COMMAND),
460              (VOID *) PtrBuffer,
461              ByteCount,
462              EfiUsbDataIn,
463              TimeOut
464              );
465
466    if (Status != EFI_SUCCESS) {
467      return Status;
468    }
469
470    Lba32 += SectorCount;
471    PtrBuffer       = (UINT8 *) PtrBuffer + SectorCount * BlockSize;
472    BlocksRemaining = (UINT16) (BlocksRemaining - SectorCount);
473  }
474
475  return Status;
476}
477
478/**
479  Check if there is media according to sense data.
480
481  @param  SenseData   Pointer to sense data.
482  @param  SenseCounts Count of sense data.
483
484  @retval TRUE    No media
485  @retval FALSE   Media exists
486
487**/
488BOOLEAN
489IsNoMedia (
490  IN  ATAPI_REQUEST_SENSE_DATA *SenseData,
491  IN  UINTN                    SenseCounts
492  )
493{
494  ATAPI_REQUEST_SENSE_DATA  *SensePtr;
495  UINTN                     Index;
496  BOOLEAN                   NoMedia;
497
498  NoMedia   = FALSE;
499  SensePtr  = SenseData;
500
501  for (Index = 0; Index < SenseCounts; Index++) {
502
503    switch (SensePtr->sense_key) {
504
505    case ATA_SK_NOT_READY:
506      switch (SensePtr->addnl_sense_code) {
507      //
508      // if no media, fill IdeDev parameter with specific info.
509      //
510      case ATA_ASC_NO_MEDIA:
511        NoMedia = TRUE;
512        break;
513
514      default:
515        break;
516      }
517      break;
518
519    default:
520      break;
521    }
522
523    SensePtr++;
524  }
525
526  return NoMedia;
527}
528
529/**
530  Check if there is media error according to sense data.
531
532  @param  SenseData   Pointer to sense data.
533  @param  SenseCounts Count of sense data.
534
535  @retval TRUE    Media error
536  @retval FALSE   No media error
537
538**/
539BOOLEAN
540IsMediaError (
541  IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,
542  IN  UINTN                       SenseCounts
543  )
544{
545  ATAPI_REQUEST_SENSE_DATA  *SensePtr;
546  UINTN                     Index;
547  BOOLEAN                   Error;
548
549  SensePtr  = SenseData;
550  Error     = FALSE;
551
552  for (Index = 0; Index < SenseCounts; Index++) {
553
554    switch (SensePtr->sense_key) {
555    //
556    // Medium error case
557    //
558    case ATA_SK_MEDIUM_ERROR:
559      switch (SensePtr->addnl_sense_code) {
560      case ATA_ASC_MEDIA_ERR1:
561        //
562        // fall through
563        //
564      case ATA_ASC_MEDIA_ERR2:
565        //
566        // fall through
567        //
568      case ATA_ASC_MEDIA_ERR3:
569        //
570        // fall through
571        //
572      case ATA_ASC_MEDIA_ERR4:
573        Error = TRUE;
574        break;
575
576      default:
577        break;
578      }
579
580      break;
581
582    //
583    // Medium upside-down case
584    //
585    case ATA_SK_NOT_READY:
586      switch (SensePtr->addnl_sense_code) {
587      case ATA_ASC_MEDIA_UPSIDE_DOWN:
588        Error = TRUE;
589        break;
590
591      default:
592        break;
593      }
594      break;
595
596    default:
597      break;
598    }
599
600    SensePtr++;
601  }
602
603  return Error;
604}
605
606/**
607  Check if media is changed according to sense data.
608
609  @param  SenseData   Pointer to sense data.
610  @param  SenseCounts Count of sense data.
611
612  @retval TRUE    There is media change event.
613  @retval FALSE   media is NOT changed.
614
615**/
616BOOLEAN
617IsMediaChange (
618  IN  ATAPI_REQUEST_SENSE_DATA *SenseData,
619  IN  UINTN                    SenseCounts
620  )
621{
622  ATAPI_REQUEST_SENSE_DATA  *SensePtr;
623  UINTN                     Index;
624  BOOLEAN                   MediaChange;
625
626  MediaChange = FALSE;
627
628  SensePtr    = SenseData;
629
630  for (Index = 0; Index < SenseCounts; Index++) {
631    //
632    // catch media change sense key and addition sense data
633    //
634    switch (SensePtr->sense_key) {
635    case ATA_SK_UNIT_ATTENTION:
636      switch (SensePtr->addnl_sense_code) {
637      case ATA_ASC_MEDIA_CHANGE:
638        MediaChange = TRUE;
639        break;
640
641      default:
642        break;
643      }
644      break;
645
646    default:
647      break;
648    }
649
650    SensePtr++;
651  }
652
653  return MediaChange;
654}
655