1/** @file
2  Implementation of the USB mass storage Control/Bulk/Interrupt transport,
3  according to USB Mass Storage Class Control/Bulk/Interrupt (CBI) Transport, Revision 1.1.
4  Notice: it is being obsoleted by the standard body in favor of the BOT
5  (Bulk-Only Transport).
6
7Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
8This program and the accompanying materials
9are licensed and made available under the terms and conditions of the BSD License
10which accompanies this distribution.  The full text of the license may be found at
11http://opensource.org/licenses/bsd-license.php
12
13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16**/
17
18#include "UsbMass.h"
19
20//
21// Definition of USB CBI0 Transport Protocol
22//
23USB_MASS_TRANSPORT mUsbCbi0Transport = {
24  USB_MASS_STORE_CBI0,
25  UsbCbiInit,
26  UsbCbiExecCommand,
27  UsbCbiResetDevice,
28  NULL,
29  UsbCbiCleanUp
30};
31
32//
33// Definition of USB CBI1 Transport Protocol
34//
35USB_MASS_TRANSPORT mUsbCbi1Transport = {
36  USB_MASS_STORE_CBI1,
37  UsbCbiInit,
38  UsbCbiExecCommand,
39  UsbCbiResetDevice,
40  NULL,
41  UsbCbiCleanUp
42};
43
44/**
45  Initializes USB CBI protocol.
46
47  This function initializes the USB mass storage class CBI protocol.
48  It will save its context which is a USB_CBI_PROTOCOL structure
49  in the Context if Context isn't NULL.
50
51  @param  UsbIo                 The USB I/O Protocol instance
52  @param  Context               The buffer to save the context to
53
54  @retval EFI_SUCCESS           The device is successfully initialized.
55  @retval EFI_UNSUPPORTED       The transport protocol doesn't support the device.
56  @retval Other                 The USB CBI initialization fails.
57
58**/
59EFI_STATUS
60UsbCbiInit (
61  IN  EFI_USB_IO_PROTOCOL   *UsbIo,
62  OUT VOID                  **Context       OPTIONAL
63  )
64{
65  USB_CBI_PROTOCOL              *UsbCbi;
66  EFI_USB_INTERFACE_DESCRIPTOR  *Interface;
67  EFI_USB_ENDPOINT_DESCRIPTOR   EndPoint;
68  EFI_STATUS                    Status;
69  UINT8                         Index;
70
71  //
72  // Allocate the CBI context for USB_CBI_PROTOCOL and 3 endpoint descriptors.
73  //
74  UsbCbi = AllocateZeroPool (
75             sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
76             );
77  ASSERT (UsbCbi != NULL);
78
79  UsbCbi->UsbIo = UsbIo;
80
81  //
82  // Get the interface descriptor and validate that it
83  // is a USB Mass Storage CBI interface.
84  //
85  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface);
86  if (EFI_ERROR (Status)) {
87    goto ON_ERROR;
88  }
89
90  Interface = &UsbCbi->Interface;
91  if ((Interface->InterfaceProtocol != USB_MASS_STORE_CBI0)
92      && (Interface->InterfaceProtocol != USB_MASS_STORE_CBI1)) {
93    Status = EFI_UNSUPPORTED;
94    goto ON_ERROR;
95  }
96
97  //
98  // Locate and save the bulk-in, bulk-out, and interrupt endpoint
99  //
100  for (Index = 0; Index < Interface->NumEndpoints; Index++) {
101    Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
102    if (EFI_ERROR (Status)) {
103      continue;
104    }
105
106    if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
107      //
108      // Use the first Bulk-In and Bulk-Out endpoints
109      //
110      if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
111         (UsbCbi->BulkInEndpoint == NULL)) {
112
113        UsbCbi->BulkInEndpoint  = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1);
114        CopyMem(UsbCbi->BulkInEndpoint, &EndPoint, sizeof (EndPoint));;
115      }
116
117      if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
118         (UsbCbi->BulkOutEndpoint == NULL)) {
119
120        UsbCbi->BulkOutEndpoint   = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 1;
121        CopyMem(UsbCbi->BulkOutEndpoint, &EndPoint, sizeof (EndPoint));
122      }
123    } else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) {
124      //
125      // Use the first interrupt endpoint if it is CBI0
126      //
127      if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) &&
128          (UsbCbi->InterruptEndpoint == NULL)) {
129
130        UsbCbi->InterruptEndpoint   = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 2;
131        CopyMem(UsbCbi->InterruptEndpoint, &EndPoint, sizeof (EndPoint));
132      }
133    }
134  }
135
136  if ((UsbCbi->BulkInEndpoint == NULL) || (UsbCbi->BulkOutEndpoint == NULL)) {
137    Status = EFI_UNSUPPORTED;
138    goto ON_ERROR;
139  }
140  if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) && (UsbCbi->InterruptEndpoint == NULL)) {
141    Status = EFI_UNSUPPORTED;
142    goto ON_ERROR;
143  }
144
145  if (Context != NULL) {
146    *Context = UsbCbi;
147  } else {
148    FreePool (UsbCbi);
149  }
150
151  return EFI_SUCCESS;
152
153ON_ERROR:
154  FreePool (UsbCbi);
155  return Status;
156}
157
158/**
159  Send the command to the device using class specific control transfer.
160
161  This function sends command to the device using class specific control transfer.
162  The CBI contains three phases: Command, Data, and Status. This is Command phase.
163
164  @param  UsbCbi                The USB CBI protocol
165  @param  Cmd                   The high level command to transfer to device
166  @param  CmdLen                The length of the command
167  @param  Timeout               The time to wait the command to finish
168
169  @retval EFI_SUCCESS           The command is sent to the device.
170  @retval Others                The command failed to transfer to device
171
172**/
173EFI_STATUS
174UsbCbiSendCommand (
175  IN USB_CBI_PROTOCOL       *UsbCbi,
176  IN UINT8                  *Cmd,
177  IN UINT8                  CmdLen,
178  IN UINT32                 Timeout
179  )
180{
181  EFI_USB_DEVICE_REQUEST  Request;
182  EFI_STATUS              Status;
183  UINT32                  TransStatus;
184  UINTN                   DataLen;
185  INTN                    Retry;
186
187  //
188  // Fill in the device request, CBI use the "Accept Device-Specific
189  // Cmd" (ADSC) class specific request to send commands.
190  //
191  Request.RequestType = 0x21;
192  Request.Request     = 0;
193  Request.Value       = 0;
194  Request.Index       = UsbCbi->Interface.InterfaceNumber;
195  Request.Length      = CmdLen;
196
197  Status              = EFI_SUCCESS;
198  Timeout             = Timeout / USB_MASS_1_MILLISECOND;
199
200  for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
201    //
202    // Use USB I/O Protocol to send the command to the device
203    //
204    TransStatus = 0;
205    DataLen     = CmdLen;
206
207    Status = UsbCbi->UsbIo->UsbControlTransfer (
208                              UsbCbi->UsbIo,
209                              &Request,
210                              EfiUsbDataOut,
211                              Timeout,
212                              Cmd,
213                              DataLen,
214                              &TransStatus
215                              );
216    //
217    // The device can fail the command by STALL the control endpoint.
218    // It can delay the command by NAK the data or status stage, this
219    // is a "class-specific exemption to the USB specification". Retry
220    // if the command is NAKed.
221    //
222    if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
223      continue;
224    }
225
226    break;
227  }
228
229  return Status;
230}
231
232
233/**
234  Transfer data between the device and host.
235
236  This function transfers data between the device and host.
237  The CBI contains three phases: Command, Data, and Status. This is Data phase.
238
239  @param  UsbCbi                The USB CBI device
240  @param  DataDir               The direction of the data transfer
241  @param  Data                  The buffer to hold the data for input or output.
242  @param  TransLen              On input, the expected transfer length.
243                                On output, the length of data actually transferred.
244  @param  Timeout               The time to wait for the command to execute
245
246  @retval EFI_SUCCESS           The data transferred successfully.
247  @retval EFI_SUCCESS           No data to transfer
248  @retval Others                Failed to transfer all the data
249
250**/
251EFI_STATUS
252UsbCbiDataTransfer (
253  IN USB_CBI_PROTOCOL         *UsbCbi,
254  IN EFI_USB_DATA_DIRECTION   DataDir,
255  IN OUT UINT8                *Data,
256  IN OUT UINTN                *TransLen,
257  IN UINT32                   Timeout
258  )
259{
260  EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
261  EFI_STATUS                  Status;
262  UINT32                      TransStatus;
263  UINTN                       Remain;
264  UINTN                       Increment;
265  UINT8                       *Next;
266  UINTN                       Retry;
267
268  //
269  // If no data to transfer, just return EFI_SUCCESS.
270  //
271  if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
272    return EFI_SUCCESS;
273  }
274
275  //
276  // Select the endpoint then issue the transfer
277  //
278  if (DataDir == EfiUsbDataIn) {
279    Endpoint = UsbCbi->BulkInEndpoint;
280  } else {
281    Endpoint = UsbCbi->BulkOutEndpoint;
282  }
283
284  Next    = Data;
285  Remain  = *TransLen;
286  Retry   = 0;
287  Status  = EFI_SUCCESS;
288  Timeout = Timeout / USB_MASS_1_MILLISECOND;
289
290  //
291  // Transfer the data with a loop. The length of data transferred once is restricted.
292  //
293  while (Remain > 0) {
294    TransStatus = 0;
295
296    if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) {
297      Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize;
298    } else {
299      Increment = Remain;
300    }
301
302    Status = UsbCbi->UsbIo->UsbBulkTransfer (
303                              UsbCbi->UsbIo,
304                              Endpoint->EndpointAddress,
305                              Next,
306                              &Increment,
307                              Timeout,
308                              &TransStatus
309                              );
310    if (EFI_ERROR (Status)) {
311      if (TransStatus == EFI_USB_ERR_NAK) {
312        //
313        // The device can NAK the host if either the data/buffer isn't
314        // aviable or the command is in-progress.
315        // If data are partially transferred, we just ignore NAK and continue.
316        // If all data have been transferred and status is NAK, then we retry for several times.
317        // If retry exceeds the USB_CBI_MAX_RETRY, then return error status.
318        //
319        if (Increment == 0) {
320          if (++Retry > USB_CBI_MAX_RETRY) {
321            goto ON_EXIT;
322          }
323        } else {
324          Next   += Increment;
325          Remain -= Increment;
326          Retry   = 0;
327        }
328
329        continue;
330      }
331
332      //
333      // The device can fail the command by STALL the bulk endpoint.
334      // Clear the stall if that is the case.
335      //
336      if (TransStatus == EFI_USB_ERR_STALL) {
337        UsbClearEndpointStall (UsbCbi->UsbIo, Endpoint->EndpointAddress);
338      }
339
340      goto ON_EXIT;
341    }
342
343    Next += Increment;
344    Remain -= Increment;
345  }
346
347ON_EXIT:
348  *TransLen -= Remain;
349  return Status;
350}
351
352
353/**
354  Gets the result of high level command execution from interrupt endpoint.
355
356  This function returns the USB transfer status, and put the high level
357  command execution result in Result.
358  The CBI contains three phases: Command, Data, and Status. This is Status phase.
359
360  @param  UsbCbi                The USB CBI protocol
361  @param  Timeout               The time to wait for the command to execute
362  @param  Result                The result of the command execution.
363
364  @retval EFI_SUCCESS           The high level command execution result is
365                                retrieved in Result.
366  @retval Others                Failed to retrieve the result.
367
368**/
369EFI_STATUS
370UsbCbiGetStatus (
371  IN  USB_CBI_PROTOCOL        *UsbCbi,
372  IN  UINT32                  Timeout,
373  OUT USB_CBI_STATUS          *Result
374  )
375{
376  UINTN                     Len;
377  UINT8                     Endpoint;
378  EFI_STATUS                Status;
379  UINT32                    TransStatus;
380  INTN                      Retry;
381
382  Endpoint  = UsbCbi->InterruptEndpoint->EndpointAddress;
383  Status    = EFI_SUCCESS;
384  Timeout   = Timeout / USB_MASS_1_MILLISECOND;
385
386  //
387  // Attemp to the read the result from interrupt endpoint
388  //
389  for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
390    TransStatus = 0;
391    Len         = sizeof (USB_CBI_STATUS);
392
393    Status = UsbCbi->UsbIo->UsbSyncInterruptTransfer (
394                              UsbCbi->UsbIo,
395                              Endpoint,
396                              Result,
397                              &Len,
398                              Timeout,
399                              &TransStatus
400                              );
401    //
402    // The CBI can NAK the interrupt endpoint if the command is in-progress.
403    //
404    if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
405      continue;
406    }
407
408    break;
409  }
410
411  return Status;
412}
413
414
415/**
416  Execute USB mass storage command through the CBI0/CBI1 transport protocol.
417
418  @param  Context               The USB CBI Protocol.
419  @param  Cmd                   The command to transfer to device
420  @param  CmdLen                The length of the command
421  @param  DataDir               The direction of data transfer
422  @param  Data                  The buffer to hold the data
423  @param  DataLen               The length of the buffer
424  @param  Lun                   Should be 0, this field for bot only
425  @param  Timeout               The time to wait
426  @param  CmdStatus             The result of the command execution
427
428  @retval EFI_SUCCESS           The command is executed successfully.
429  @retval Other                 Failed to execute the command
430
431**/
432EFI_STATUS
433UsbCbiExecCommand (
434  IN  VOID                    *Context,
435  IN  VOID                    *Cmd,
436  IN  UINT8                   CmdLen,
437  IN  EFI_USB_DATA_DIRECTION  DataDir,
438  IN  VOID                    *Data,
439  IN  UINT32                  DataLen,
440  IN  UINT8                   Lun,
441  IN  UINT32                  Timeout,
442  OUT UINT32                  *CmdStatus
443  )
444{
445  USB_CBI_PROTOCOL          *UsbCbi;
446  USB_CBI_STATUS            Result;
447  EFI_STATUS                Status;
448  UINTN                     TransLen;
449
450  *CmdStatus  = USB_MASS_CMD_SUCCESS;
451  UsbCbi      = (USB_CBI_PROTOCOL *) Context;
452
453  //
454  // Send the command to the device. Return immediately if device
455  // rejects the command.
456  //
457  Status = UsbCbiSendCommand (UsbCbi, Cmd, CmdLen, Timeout);
458  if (EFI_ERROR (Status)) {
459    gBS->Stall(10 * USB_MASS_1_MILLISECOND);
460    DEBUG ((EFI_D_ERROR, "UsbCbiExecCommand: UsbCbiSendCommand (%r)\n",Status));
461    return Status;
462  }
463
464  //
465  // Transfer the data. Return this status if no interrupt endpoint
466  // is used to report the transfer status.
467  //
468  TransLen = (UINTN) DataLen;
469
470  Status   = UsbCbiDataTransfer (UsbCbi, DataDir, Data, &TransLen, Timeout);
471  if (UsbCbi->InterruptEndpoint == NULL) {
472    DEBUG ((EFI_D_ERROR, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n",Status));
473    return Status;
474  }
475
476  //
477  // Get the status. If it succeeds, interpret the result.
478  //
479  Status = UsbCbiGetStatus (UsbCbi, Timeout, &Result);
480  if (EFI_ERROR (Status)) {
481    DEBUG ((EFI_D_ERROR, "UsbCbiExecCommand: UsbCbiGetStatus (%r)\n",Status));
482    return Status;
483  }
484
485  if (UsbCbi->Interface.InterfaceSubClass == USB_MASS_STORE_UFI) {
486    //
487    // For UFI device, ASC and ASCQ are returned.
488    //
489    // Do not set the USB_MASS_CMD_FAIL for a request sense command
490    // as a bad result type doesn't mean a cmd failure
491    //
492    if (Result.Type != 0 && *(UINT8*)Cmd != 0x03) {
493      *CmdStatus = USB_MASS_CMD_FAIL;
494    }
495  } else {
496    //
497    // Check page 27, CBI spec 1.1 for vaious reture status.
498    //
499    switch (Result.Value & 0x03) {
500    case 0x00:
501      //
502      // Pass
503      //
504      *CmdStatus = USB_MASS_CMD_SUCCESS;
505      break;
506
507    case 0x02:
508      //
509      // Phase Error, response with reset.
510      // No break here to fall through to "Fail".
511      //
512      UsbCbiResetDevice (UsbCbi, FALSE);
513
514    case 0x01:
515      //
516      // Fail
517      //
518      *CmdStatus = USB_MASS_CMD_FAIL;
519      break;
520
521    case 0x03:
522      //
523      // Persistent Fail. Need to send REQUEST SENSE.
524      //
525      *CmdStatus = USB_MASS_CMD_PERSISTENT;
526      break;
527    }
528  }
529
530  return EFI_SUCCESS;
531}
532
533
534/**
535  Reset the USB mass storage device by CBI protocol.
536
537  This function resets the USB mass storage device by CBI protocol.
538  The reset is defined as a non-data command. Don't use UsbCbiExecCommand
539  to send the command to device because that may introduce recursive loop.
540
541  @param  Context               The USB CBI protocol
542  @param  ExtendedVerification  The flag controlling the rule of reset.
543                                Not used here.
544
545  @retval EFI_SUCCESS           The device is reset.
546  @retval Others                Failed to reset the device.
547
548**/
549EFI_STATUS
550UsbCbiResetDevice (
551  IN  VOID                    *Context,
552  IN  BOOLEAN                  ExtendedVerification
553  )
554{
555  UINT8                     ResetCmd[USB_CBI_RESET_CMD_LEN];
556  USB_CBI_PROTOCOL          *UsbCbi;
557  USB_CBI_STATUS            Result;
558  EFI_STATUS                Status;
559  UINT32                    Timeout;
560
561  UsbCbi = (USB_CBI_PROTOCOL *) Context;
562
563  //
564  // Fill in the reset command.
565  //
566  SetMem (ResetCmd, USB_CBI_RESET_CMD_LEN, 0xFF);
567
568  ResetCmd[0] = 0x1D;
569  ResetCmd[1] = 0x04;
570  Timeout     = USB_CBI_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
571
572  //
573  // Send the command to the device. Don't use UsbCbiExecCommand here.
574  //
575  Status = UsbCbiSendCommand (UsbCbi, ResetCmd, USB_CBI_RESET_CMD_LEN, Timeout);
576  if (EFI_ERROR (Status)) {
577    return EFI_DEVICE_ERROR;
578  }
579
580  //
581  // Just retrieve the status and ignore that. Then stall
582  // 50ms to wait for it to complete.
583  //
584  UsbCbiGetStatus (UsbCbi, Timeout, &Result);
585  gBS->Stall (USB_CBI_RESET_DEVICE_STALL);
586
587  //
588  // Clear the Bulk-In and Bulk-Out stall condition and init data toggle.
589  //
590  UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkInEndpoint->EndpointAddress);
591  UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkOutEndpoint->EndpointAddress);
592
593  return Status;
594}
595
596
597/**
598  Clean up the CBI protocol's resource.
599
600  @param  Context               The instance of CBI protocol.
601
602  @retval EFI_SUCCESS           The resource is cleaned up.
603
604**/
605EFI_STATUS
606UsbCbiCleanUp (
607  IN  VOID                   *Context
608  )
609{
610  FreePool (Context);
611  return EFI_SUCCESS;
612}
613