1292932Sdim/*-
2292932Sdim * This file is provided under a dual BSD/GPLv2 license.  When using or
3292932Sdim * redistributing this file, you may do so under either license.
4292932Sdim*
5292932Sdim* GPL LICENSE SUMMARY
6292932Sdim*
7292932Sdim* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8292932Sdim*
9292932Sdim* This program is free software; you can redistribute it and/or modify
10292932Sdim* it under the terms of version 2 of the GNU General Public License as
11292932Sdim* published by the Free Software Foundation.
12292932Sdim*
13292932Sdim* This program is distributed in the hope that it will be useful, but
14309124Sdim* WITHOUT ANY WARRANTY; without even the implied warranty of
15309124Sdim* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16309124Sdim* General Public License for more details.
17309124Sdim*
18309124Sdim* You should have received a copy of the GNU General Public License
19292932Sdim* along with this program; if not, write to the Free Software
20292932Sdim* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21292932Sdim* The full GNU General Public License is included in this distribution
22292932Sdim* in the file called LICENSE.GPL.
23292932Sdim*
24292932Sdim* BSD LICENSE
25292932Sdim*
26292932Sdim* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27292932Sdim* All rights reserved.
28292932Sdim*
29292932Sdim* Redistribution and use in source and binary forms, with or without
30292932Sdim* modification, are permitted provided that the following conditions
31292932Sdim* are met:
32292932Sdim*
33292932Sdim*   * Redistributions of source code must retain the above copyright
34292932Sdim*     notice, this list of conditions and the following disclaimer.
35292932Sdim*   * Redistributions in binary form must reproduce the above copyright
36309124Sdim*     notice, this list of conditions and the following disclaimer in
37309124Sdim*     the documentation and/or other materials provided with the
38309124Sdim*     distribution.
39309124Sdim*
40309124Sdim* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
41309124Sdim* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
42309124Sdim* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
43292932Sdim* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
44292932Sdim* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45292932Sdim* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46292932Sdim* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47292932Sdim* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48292932Sdim* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49292932Sdim* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50292932Sdim* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51292932Sdim */
52292932Sdim
53292932Sdim#include <sys/cdefs.h>
54292932Sdim__FBSDID("$FreeBSD$");
55292932Sdim
56292932Sdim/**
57292932Sdim* @file
58292932Sdim* @brief This file contains the method implementations to translate
59292932Sdim*        SCSI Write Buffer command based of the SAT2v07 spec.
60292932Sdim*/
61292932Sdim
62292932Sdim#include <dev/isci/scil/sati_write_buffer.h>
63292932Sdim#include <dev/isci/scil/sati_callbacks.h>
64292932Sdim#include <dev/isci/scil/sati_util.h>
65292932Sdim
66292932Sdim#define WRITE_BUFFER_WRITE_DATA              0x02
67292932Sdim#define WRITE_BUFFER_DOWNLOAD_SAVE           0x05
68292932Sdim#define WRITE_BUFFER_OFFSET_DOWNLOAD_SAVE    0x07
69292932Sdim#define DOWNLOAD_MICROCODE_BLOCK_SIZE        512
70292932Sdim
71292932Sdim/**
72292932Sdim* @brief This method will translate the SCSI Write Buffer command
73292932Sdim*        into a corresponding ATA Write Buffer and Download Microcode commands.
74292932Sdim*        For more information on the parameters passed to this method,
75292932Sdim*        please reference sati_translate_command().
76292932Sdim*
77292932Sdim* @return Indicates if the command translation succeeded.
78292932Sdim* @retval SATI_SUCCESS indicates that the translation was supported and occurred
79292932Sdim*         without error.
80309124Sdim* @retval SATI_FAILURE_CHECK_RESPONSE_DATA This value is returned if
81309124Sdim*         there is a translation failure.
82309124Sdim*/
83309124SdimSATI_STATUS sati_write_buffer_translate_command(
84292932Sdim   SATI_TRANSLATOR_SEQUENCE_T * sequence,
85292932Sdim   void                       * scsi_io,
86292932Sdim   void                       * ata_io
87292932Sdim)
88292932Sdim{
89292932Sdim   U8 * cdb = sati_cb_get_cdb_address(scsi_io);
90292932Sdim   SATI_STATUS status = SATI_FAILURE;
91292932Sdim   U32 allocation_length;
92292932Sdim   U32 allocation_blocks;
93292932Sdim   U32 buffer_offset;
94292932Sdim
95292932Sdim   allocation_length = ((sati_get_cdb_byte(cdb, 6) << 16) |
96292932Sdim                        (sati_get_cdb_byte(cdb, 7) << 8)  |
97309124Sdim                        (sati_get_cdb_byte(cdb, 8)));
98292932Sdim
99309124Sdim   buffer_offset = ((sati_get_cdb_byte(cdb, 3) << 16) |
100292932Sdim                    (sati_get_cdb_byte(cdb, 4) << 8)  |
101309124Sdim                    (sati_get_cdb_byte(cdb, 5)));
102309124Sdim
103309124Sdim   sequence->allocation_length = allocation_length;
104309124Sdim   allocation_blocks = allocation_length / DOWNLOAD_MICROCODE_BLOCK_SIZE;
105309124Sdim
106292932Sdim   switch(sati_get_cdb_byte(cdb, 1))
107309124Sdim   {
108292932Sdim      case WRITE_BUFFER_WRITE_DATA:
109309124Sdim         if((allocation_length == DOWNLOAD_MICROCODE_BLOCK_SIZE) &&
110309124Sdim            (buffer_offset == 0) &&
111309124Sdim            (sati_get_cdb_byte(cdb, 2) == 0))
112309124Sdim         {
113309124Sdim            sati_ata_write_buffer_construct(ata_io, sequence);
114292932Sdim            sequence->type = SATI_SEQUENCE_WRITE_BUFFER;
115292932Sdim            sequence->state = SATI_SEQUENCE_STATE_AWAIT_RESPONSE;
116292932Sdim            status = SATI_SUCCESS;
117292932Sdim         }
118292932Sdim         else
119292932Sdim         {
120292932Sdim            sati_scsi_sense_data_construct(
121292932Sdim               sequence,
122292932Sdim               scsi_io,
123292932Sdim               SCSI_STATUS_CHECK_CONDITION,
124292932Sdim               SCSI_SENSE_ILLEGAL_REQUEST,
125292932Sdim               SCSI_ASC_INVALID_FIELD_IN_CDB,
126292932Sdim               SCSI_ASCQ_INVALID_FIELD_IN_CDB
127292932Sdim            );
128292932Sdim
129292932Sdim            sequence->state = SATI_SEQUENCE_STATE_FINAL;
130292932Sdim            status = SATI_FAILURE_CHECK_RESPONSE_DATA;
131292932Sdim         }
132292932Sdim      break;
133292932Sdim
134292932Sdim      case WRITE_BUFFER_DOWNLOAD_SAVE:
135292932Sdim
136292932Sdim         sati_ata_download_microcode_construct(
137292932Sdim            ata_io,
138292932Sdim            sequence,
139292932Sdim            ATA_MICROCODE_DOWNLOAD_SAVE,
140292932Sdim            allocation_length,
141292932Sdim            buffer_offset
142292932Sdim         );
143292932Sdim
144292932Sdim         sequence->type = SATI_SEQUENCE_WRITE_BUFFER_MICROCODE;
145292932Sdim         sequence->state = SATI_SEQUENCE_STATE_AWAIT_RESPONSE;
146292932Sdim         status = SATI_SUCCESS;
147292932Sdim      break;
148292932Sdim
149292932Sdim      case WRITE_BUFFER_OFFSET_DOWNLOAD_SAVE:
150292932Sdim         if(((allocation_length & 0x000001FF) == 0) && //Bits 08:00 need to be zero per SAT2v7
151292932Sdim            ((buffer_offset & 0x000001FF) == 0)     &&
152292932Sdim            (allocation_blocks <= sequence->device->max_blocks_per_microcode_command) &&
153292932Sdim            ((allocation_blocks >= sequence->device->min_blocks_per_microcode_command) ||
154292932Sdim            (allocation_length == 0)))
155292932Sdim         {
156292932Sdim            sati_ata_download_microcode_construct(
157292932Sdim               ata_io,
158292932Sdim               sequence,
159292932Sdim               ATA_MICROCODE_OFFSET_DOWNLOAD,
160292932Sdim               allocation_length,
161292932Sdim               buffer_offset
162292932Sdim            );
163292932Sdim
164292932Sdim            sequence->type = SATI_SEQUENCE_WRITE_BUFFER_MICROCODE;
165292932Sdim            sequence->state = SATI_SEQUENCE_STATE_AWAIT_RESPONSE;
166292932Sdim            status = SATI_SUCCESS;
167292932Sdim         }
168292932Sdim         else
169292932Sdim         {
170292932Sdim            sati_scsi_sense_data_construct(
171292932Sdim               sequence,
172292932Sdim               scsi_io,
173292932Sdim               SCSI_STATUS_CHECK_CONDITION,
174292932Sdim               SCSI_SENSE_ILLEGAL_REQUEST,
175292932Sdim               SCSI_ASC_INVALID_FIELD_IN_CDB,
176292932Sdim               SCSI_ASCQ_INVALID_FIELD_IN_CDB
177292932Sdim            );
178292932Sdim
179292932Sdim            sequence->state = SATI_SEQUENCE_STATE_FINAL;
180292932Sdim            status = SATI_FAILURE_CHECK_RESPONSE_DATA;
181292932Sdim         }
182292932Sdim      break;
183292932Sdim
184292932Sdim      default: //unsupported Write Buffer Mode
185292932Sdim         sati_scsi_sense_data_construct(
186292932Sdim            sequence,
187292932Sdim            scsi_io,
188292932Sdim            SCSI_STATUS_CHECK_CONDITION,
189292932Sdim            SCSI_SENSE_ILLEGAL_REQUEST,
190292932Sdim            SCSI_ASC_INVALID_FIELD_IN_CDB,
191292932Sdim            SCSI_ASCQ_INVALID_FIELD_IN_CDB
192292932Sdim         );
193292932Sdim
194292932Sdim         sequence->state = SATI_SEQUENCE_STATE_FINAL;
195292932Sdim         status = SATI_FAILURE_CHECK_RESPONSE_DATA;
196292932Sdim      break;
197292932Sdim   }
198292932Sdim   return status;
199292932Sdim}
200292932Sdim
201292932Sdim/**
202292932Sdim* @brief This method will complete the Write Buffer Translation by checking
203292932Sdim*        for ATA errors and then creating a unit attention condition for
204292932Sdim*        changed microcode.
205292932Sdim*
206292932Sdim* @return Indicates if the command translation succeeded.
207292932Sdim* @retval SATI_FAILURE_CHECK_RESPONSE_DATA This value is returned if
208292932Sdim*         there is a translation failure.
209292932Sdim* @retval SATI_COMPLETE indicates that the translation was supported, occurred without
210292932Sdim*         error, and no additional translation is necessary.
211292932Sdim*/
212292932SdimSATI_STATUS sati_write_buffer_translate_response(
213292932Sdim   SATI_TRANSLATOR_SEQUENCE_T * sequence,
214292932Sdim   void                       * scsi_io,
215292932Sdim   void                       * ata_io
216292932Sdim)
217292932Sdim{
218292932Sdim   U8 * register_fis = sati_cb_get_d2h_register_fis_address(ata_io);
219292932Sdim   U8 ata_status = (U8) sati_get_ata_status(register_fis);
220292932Sdim   SATI_STATUS status = SATI_FAILURE;
221292932Sdim
222292932Sdim   if (ata_status & ATA_STATUS_REG_ERROR_BIT)
223292932Sdim   {
224292932Sdim      sati_scsi_sense_data_construct(
225292932Sdim         sequence,
226292932Sdim         scsi_io,
227292932Sdim         SCSI_STATUS_CHECK_CONDITION,
228292932Sdim         SCSI_SENSE_ABORTED_COMMAND,
229292932Sdim         SCSI_ASC_NO_ADDITIONAL_SENSE,
230292932Sdim         SCSI_ASCQ_NO_ADDITIONAL_SENSE
231292932Sdim      );
232292932Sdim      status = SATI_FAILURE_CHECK_RESPONSE_DATA;
233292932Sdim   }
234292932Sdim   else
235292932Sdim   {
236292932Sdim      switch(sequence->type)
237292932Sdim      {
238292932Sdim         case SATI_SEQUENCE_WRITE_BUFFER_MICROCODE:
239292932Sdim            sati_scsi_sense_data_construct(
240292932Sdim               sequence,
241292932Sdim               scsi_io,
242292932Sdim               SCSI_STATUS_GOOD,
243292932Sdim               SCSI_SENSE_UNIT_ATTENTION,
244292932Sdim               SCSI_ASC_MICROCODE_HAS_CHANGED,
245292932Sdim               SCSI_ASCQ_MICROCODE_HAS_CHANGED
246292932Sdim            );
247292932Sdim            status = SATI_COMPLETE;
248292932Sdim         break;
249292932Sdim
250292932Sdim         default:
251292932Sdim            status = SATI_COMPLETE;
252292932Sdim         break;
253292932Sdim      }
254292932Sdim   }
255292932Sdim
256292932Sdim   sequence->state = SATI_SEQUENCE_STATE_FINAL;
257292932Sdim   return status;
258292932Sdim}
259292932Sdim