1 /*-
2 * This file is provided under a dual BSD/GPLv2 license.  When using or
3 * redistributing this file, you may do so under either license.
4 *
5 * GPL LICENSE SUMMARY
6 *
7 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of version 2 of the GNU General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21 * The full GNU General Public License is included in this distribution
22 * in the file called LICENSE.GPL.
23 *
24 * BSD LICENSE
25 *
26 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27 * All rights reserved.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 *
33 *   * Redistributions of source code must retain the above copyright
34 *     notice, this list of conditions and the following disclaimer.
35 *   * Redistributions in binary form must reproduce the above copyright
36 *     notice, this list of conditions and the following disclaimer in
37 *     the documentation and/or other materials provided with the
38 *     distribution.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
41 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
42 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
43 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
44 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 */
52
53#include <sys/cdefs.h>
54__FBSDID("$FreeBSD$");
55
56/**
57 * @file
58 * @brief This file contains the method implementations required to
59 *        translate the SCSI mode sense (6 and 10-byte) commands.
60 */
61
62#if !defined(DISABLE_SATI_MODE_SENSE)
63
64#include <dev/isci/scil/sati_mode_sense.h>
65#include <dev/isci/scil/sati_mode_pages.h>
66#include <dev/isci/scil/sati_callbacks.h>
67#include <dev/isci/scil/sati_util.h>
68#include <dev/isci/scil/intel_scsi.h>
69#include <dev/isci/scil/intel_ata.h>
70
71//******************************************************************************
72//* P R I V A T E   M E T H O D S
73//******************************************************************************
74
75#define STANDBY_TIMER_DISABLED  0x00
76#define STANDBY_TIMER_ENABLED   0x01
77#define STANDBY_TIMER_SUPPORTED 0x2000
78
79
80
81/**
82 * @brief This method indicates if the supplied page control is supported
83 *        by this translation implementation.  Currently savable parameters
84 *        (i.e. non-volatile) are not supported.
85 *        For more information on the parameters passed to this method,
86 *        please reference sati_translate_command().
87 *
88 * @return This method returns an indication of whether the page control
89 *         specified in the SCSI CDB is supported.
90 * @retval SATI_SUCCESS This value is returned if the page control is
91 *         supported.
92 * @retval SATI_FAILURE_CHECK_RESPONSE_DATA This value is returned if the
93 *         page control is not supported.
94 */
95static
96SATI_STATUS sati_mode_sense_is_page_control_supported(
97   SATI_TRANSLATOR_SEQUENCE_T * sequence,
98   void                       * scsi_io
99)
100{
101   U8 * cdb = sati_cb_get_cdb_address(scsi_io);
102
103   switch (sati_get_cdb_byte(cdb, 2) >> SCSI_MODE_SENSE_PC_SHIFT)
104   {
105      case SCSI_MODE_SENSE_PC_CURRENT:
106      case SCSI_MODE_SENSE_PC_DEFAULT:
107      case SCSI_MODE_SENSE_PC_CHANGEABLE:
108         return SATI_SUCCESS;
109      break;
110
111      default:
112      case SCSI_MODE_SENSE_PC_SAVED:
113         sati_scsi_sense_data_construct(
114            sequence,
115            scsi_io,
116            SCSI_STATUS_CHECK_CONDITION,
117            SCSI_SENSE_ILLEGAL_REQUEST,
118            SCSI_ASC_SAVING_PARMS_NOT_SUPPORTED,
119            SCSI_ASCQ_SAVING_PARMS_NOT_SUPPORTED
120         );
121         return SATI_FAILURE_CHECK_RESPONSE_DATA;
122      break;
123   }
124}
125
126/**
127 * @brief This method indicates if the page code field in the SCSI CDB
128 *        is supported by this translation.
129 *        For more information on the parameters passed to this method,
130 *        please reference sati_translate_command().
131 *
132 * @param[in] cdb_length This parameter specifies the length of the SCSI
133 *            CDB being translated (e.g. 6-byte, 10-byte, 12-byte, etc.)
134 *
135 * @return This method returns an indication as to whether the page code
136 *         in the CDB is supported.
137 * @retval SATI_SUCCESS This value is returned if the page code is
138 *         supported.
139 * @retval SATI_FAILURE_CHECK_RESPONSE_DATA This value is returned if the
140 *         page code is not supported.
141 */
142static
143SATI_STATUS sati_mode_sense_is_page_code_supported(
144   SATI_TRANSLATOR_SEQUENCE_T * sequence,
145   void                       * scsi_io,
146   U8                           cdb_length
147)
148{
149   U8 * cdb = sati_cb_get_cdb_address(scsi_io);
150
151   switch (sati_get_cdb_byte(cdb, 2) & SCSI_MODE_SENSE_PAGE_CODE_ENABLE)
152   {
153      case SCSI_MODE_PAGE_CACHING:
154         if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
155            sequence->type = SATI_SEQUENCE_MODE_SENSE_6_CACHING;
156         else
157            sequence->type = SATI_SEQUENCE_MODE_SENSE_10_CACHING;
158      break;
159
160      case SCSI_MODE_PAGE_ALL_PAGES:
161         if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
162            sequence->type = SATI_SEQUENCE_MODE_SENSE_6_ALL_PAGES;
163         else
164            sequence->type = SATI_SEQUENCE_MODE_SENSE_10_ALL_PAGES;
165      break;
166
167      case SCSI_MODE_PAGE_READ_WRITE_ERROR:
168         if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
169            sequence->type = SATI_SEQUENCE_MODE_SENSE_6_READ_WRITE_ERROR;
170         else
171            sequence->type = SATI_SEQUENCE_MODE_SENSE_10_READ_WRITE_ERROR;
172      break;
173
174      case SCSI_MODE_PAGE_DISCONNECT_RECONNECT:
175         if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
176            sequence->type = SATI_SEQUENCE_MODE_SENSE_6_DISCONNECT_RECONNECT;
177         else
178            sequence->type = SATI_SEQUENCE_MODE_SENSE_10_DISCONNECT_RECONNECT;
179      break;
180
181      case SCSI_MODE_PAGE_CONTROL:
182         if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
183            sequence->type = SATI_SEQUENCE_MODE_SENSE_6_CONTROL;
184         else
185            sequence->type = SATI_SEQUENCE_MODE_SENSE_10_CONTROL;
186      break;
187
188      case SCSI_MODE_PAGE_POWER_CONDITION:
189         if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
190            sequence->type = SATI_SEQUENCE_MODE_SENSE_6_POWER_CONDITION;
191         else
192            sequence->type = SATI_SEQUENCE_MODE_SENSE_10_POWER_CONDITION;
193      break;
194
195      case SCSI_MODE_PAGE_INFORMATIONAL_EXCP_CONTROL:
196         // The informational exceptions control page is only useful
197         // if SMART is supported.
198         if ((sequence->device->capabilities | SATI_DEVICE_CAP_SMART_SUPPORT)
199             == 0)
200         {
201            // For a MODE SENSE, utilize INVALID FIELD IN CDB,
202            // For a MODE SELECT, utilize INVALID FIELD IN PARAMETER LIST.
203            if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
204            {
205               sati_scsi_sense_data_construct(
206                  sequence,
207                  scsi_io,
208                  SCSI_STATUS_CHECK_CONDITION,
209                  SCSI_SENSE_ILLEGAL_REQUEST,
210                  SCSI_ASC_INVALID_FIELD_IN_CDB,
211                  SCSI_ASCQ_INVALID_FIELD_IN_CDB
212               );
213            }
214            else
215            {
216               sati_scsi_sense_data_construct(
217                  sequence,
218                  scsi_io,
219                  SCSI_STATUS_CHECK_CONDITION,
220                  SCSI_SENSE_ILLEGAL_REQUEST,
221                  SCSI_ASC_INVALID_FIELD_IN_PARM_LIST,
222                  SCSI_ASCQ_INVALID_FIELD_IN_PARM_LIST
223               );
224            }
225
226            return SATI_FAILURE_CHECK_RESPONSE_DATA;
227         }
228
229         if (sati_get_cdb_byte(cdb, 0) == SCSI_MODE_SENSE_6)
230            sequence->type = SATI_SEQUENCE_MODE_SENSE_6_INFORMATIONAL_EXCP_CONTROL;
231         else
232            sequence->type = SATI_SEQUENCE_MODE_SENSE_10_INFORMATIONAL_EXCP_CONTROL;
233      break;
234
235      default:
236         sati_scsi_sense_data_construct(
237            sequence,
238            scsi_io,
239            SCSI_STATUS_CHECK_CONDITION,
240            SCSI_SENSE_ILLEGAL_REQUEST,
241            SCSI_ASC_INVALID_FIELD_IN_CDB,
242            SCSI_ASCQ_INVALID_FIELD_IN_CDB
243         );
244         return SATI_FAILURE_CHECK_RESPONSE_DATA;
245      break;
246   }
247
248   return SATI_SUCCESS;
249}
250
251//******************************************************************************
252//* P R O T E C T E D   M E T H O D S
253//******************************************************************************
254
255/**
256 * @brief This method will calculate the size of the mode sense data header.
257 *        This includes the block descriptor if one is requested.
258 *
259 * @param[in] scsi_io This parameter specifies the user's SCSI IO object
260 *            for which to calculate the mode page header.
261 * @param[in] cdb_size This parameter specifies the number of bytes
262 *            associated with the CDB for which to calculate the header.
263 *
264 * @return This method returns the size, in bytes, for the mode page header.
265 */
266U16 sati_mode_sense_calculate_page_header(
267   void * scsi_io,
268   U8     cdb_size
269)
270{
271   U8 * cdb         = sati_cb_get_cdb_address(scsi_io);
272   U16  page_length = 0;
273
274   // The Mode page header length is different for 6-byte vs. 10-byte CDBs.
275   if (cdb_size == 6)
276      page_length += SCSI_MODE_SENSE_6_HEADER_LENGTH;
277   else
278      page_length += SCSI_MODE_SENSE_10_HEADER_LENGTH;
279
280   // Are block descriptors disabled (DBD)?  0 indicates they are enabled.
281   if ((sati_get_cdb_byte(cdb, 1) & SCSI_MODE_SENSE_DBD_ENABLE) == 0)
282   {
283      // The LLBAA bit is not defined for 6-byte mode sense requests.
284      if (  (cdb_size == 10)
285         && (sati_get_cdb_byte(cdb, 1) & SCSI_MODE_SENSE_LLBAA_ENABLE) )
286         page_length += SCSI_MODE_SENSE_LLBA_BLOCK_DESCRIPTOR_LENGTH;
287      else
288         page_length += SCSI_MODE_SENSE_STD_BLOCK_DESCRIPTOR_LENGTH;
289   }
290
291   return page_length;
292}
293
294/**
295 * @brief This method performs command translation common to all mode sense
296 *        requests (6 or 10 byte).
297 *        For more information on the parameters passed to this method,
298 *        please reference sati_translate_command().
299 *
300 * @param[in] cdb_length This parameter specifies the number of bytes
301 *            in the CDB (6 or 10).
302 *
303 * @return This method returns an indication as to whether the translation
304 *         succeeded.
305 * @retval SCI_SUCCESS This value is returned if translation succeeded.
306 * @see sati_mode_sense_is_page_control_supported() or
307 *      sati_mode_sense_is_page_code_supported() for more information.
308 */
309SATI_STATUS sati_mode_sense_translate_command(
310   SATI_TRANSLATOR_SEQUENCE_T * sequence,
311   void                       * scsi_io,
312   void                       * ata_io,
313   U8                           cdb_length
314)
315{
316   SATI_STATUS   status;
317
318   /**
319    * Validate that the supplied page control (PC) field is supported.
320    */
321   status = sati_mode_sense_is_page_control_supported(sequence, scsi_io);
322   if (status != SATI_SUCCESS)
323      return status;
324
325   /**
326    * Validate that the supplied page code is supported.
327    */
328   status = sati_mode_sense_is_page_code_supported(sequence,scsi_io,cdb_length);
329   if (status != SATI_SUCCESS)
330      return status;
331
332   sati_ata_identify_device_construct(ata_io, sequence);
333
334   return SATI_SUCCESS;
335}
336
337/**
338 * @brief This method will build the standard block descriptor for a MODE
339 *        SENSE 6 or 10 byte request.
340 *        For more information on the parameters passed to this method,
341 *        please reference sati_translate_command().
342 *
343 * @param[in] identify This parameter specifies the IDENTIFY DEVICE data
344 *            associated with the SCSI IO.
345 * @param[in] offset This parameter specifies the offset into the data
346 *            buffer at which to build the block descriptor.
347 *
348 * @return This method returns the size of the block descriptor built.
349 */
350U32 sati_mode_sense_build_std_block_descriptor(
351   SATI_TRANSLATOR_SEQUENCE_T * sequence,
352   void                       * scsi_io,
353   ATA_IDENTIFY_DEVICE_DATA_T * identify,
354   U32                          offset
355)
356{
357   U32  lba_low     = 0;
358   U32  lba_high    = 0;
359   U32  sector_size = 0;
360
361   // Extract the sector information (sector size, logical blocks) from
362   // the retrieved ATA identify device data.
363   sati_ata_identify_device_get_sector_info(
364      identify, &lba_high, &lba_low, &sector_size
365   );
366
367   // Fill in the 4-byte logical block address field.
368   sati_set_data_byte(sequence, scsi_io, offset,   (U8)((lba_low>>24) & 0xFF));
369   sati_set_data_byte(sequence, scsi_io, offset+1, (U8)((lba_low>>16) & 0xFF));
370   sati_set_data_byte(sequence, scsi_io, offset+2, (U8)((lba_low>>8)  & 0xFF));
371   sati_set_data_byte(sequence, scsi_io, offset+3, (U8)(lba_low & 0xFF));
372
373   // Clear the reserved field.
374   sati_set_data_byte(sequence, scsi_io, offset+4, 0);
375
376   // Fill in the three byte Block Length field
377   sati_set_data_byte(sequence,scsi_io, offset+5, (U8)((sector_size>>16) & 0xFF));
378   sati_set_data_byte(sequence,scsi_io, offset+6, (U8)((sector_size>>8)  & 0xFF));
379   sati_set_data_byte(sequence,scsi_io, offset+7, (U8)(sector_size & 0xFF));
380
381   return SCSI_MODE_SENSE_STD_BLOCK_DESCRIPTOR_LENGTH;
382}
383
384/**
385 * @brief This method simply copies the mode sense data into the buffer
386 *        at the location specified by page_start.  The buffer copied is
387 *        determined by page_control (e.g. current, default, or changeable
388 *        values).
389 *        For more information on the parameters passed to this method,
390 *        please reference sati_translate_command().
391 *
392 * @param[in] page_start This parameter specifies the starting offset at
393 *            which to copy the mode page data.
394 * @param[in] page_control This parameter specifies the page control
395 *            indicating the source buffer to be copied.
396 * @param[in] page_code This specifies the mode sense page to copy.
397 *
398 * @return This method returns the size of the mode page data being copied.
399 */
400U32 sati_mode_sense_copy_initial_data(
401   SATI_TRANSLATOR_SEQUENCE_T * sequence,
402   void                       * scsi_io,
403   U32                          page_start,
404   U8                           page_control,
405   U8                           page_code
406)
407{
408   U16 page_index  = sati_mode_page_get_page_index(page_code);
409   U32 page_length = sat_mode_page_sizes[page_index];
410
411   // Find out if the current values are requested or if the default
412   // values are being requested.
413   if (page_control == SCSI_MODE_SENSE_PC_CHANGEABLE)
414   {
415      // Copy the changeable mode page information.
416      sati_copy_data(
417         sequence,
418         scsi_io,
419         page_start,
420         sat_changeable_mode_pages[page_index],
421         page_length
422      );
423   }
424   else
425   {
426      // Copy the default static values template to the user data area.
427      sati_copy_data(
428         sequence,
429         scsi_io,
430         page_start,
431         sat_default_mode_pages[page_index],
432         page_length
433      );
434   }
435
436   return page_length;
437}
438
439/**
440 * @brief This method performs the read/write error recovery mode page
441 *        specific data translation based upon the contents of the remote
442 *        device IDENTIFY DEVICE data.
443 *        For more information on the parameters passed to this method,
444 *        please reference sati_translate_command().
445 *
446 * @param[in] identify This parameter specifies the remote device's
447 *            IDENTIFY DEVICE data received as part of the IO request.
448 * @param[in] offset This parameter specifies the offset into the data
449 *            buffer where the translated data is to be written.
450 *
451 * @return This method returns the size of the mode page data that was
452 *         translated.
453 */
454U32 sati_mode_sense_read_write_error_translate_data(
455   SATI_TRANSLATOR_SEQUENCE_T * sequence,
456   void                       * scsi_io,
457   ATA_IDENTIFY_DEVICE_DATA_T * identify,
458   U32                          offset
459)
460{
461   U8 * cdb          = sati_cb_get_cdb_address(scsi_io);
462   U8   page_control = sati_get_cdb_byte(cdb, 2) >> SCSI_MODE_SENSE_PC_SHIFT;
463   U32  page_length;
464
465   page_length = sati_mode_sense_copy_initial_data(
466                    sequence,
467                    scsi_io,
468                    offset,
469                    page_control,
470                    SCSI_MODE_PAGE_READ_WRITE_ERROR
471                 );
472
473   // Currently we do not override any bits in this mode page from the
474   // identify data.
475
476   return page_length;
477}
478
479/**
480 * @brief This method performs the disconnect/reconnect mode page
481 *        specific data translation based upon the contents of the remote
482 *        device IDENTIFY DEVICE data.
483 *        For more information on the parameters passed to this method,
484 *        please reference sati_translate_command().
485 *
486 * @param[in] identify This parameter specifies the remote device's
487 *            IDENTIFY DEVICE data received as part of the IO request.
488 * @param[in] offset This parameter specifies the offset into the data
489 *            buffer where the translated data is to be written.
490 *
491 * @return This method returns the size of the mode page data that was
492 *         translated.
493 */
494U32 sati_mode_sense_disconnect_reconnect_translate_data(
495   SATI_TRANSLATOR_SEQUENCE_T * sequence,
496   void                       * scsi_io,
497   ATA_IDENTIFY_DEVICE_DATA_T * identify,
498   U32                          offset
499)
500{
501   U8 * cdb          = sati_cb_get_cdb_address(scsi_io);
502   U8   page_control = sati_get_cdb_byte(cdb, 2) >> SCSI_MODE_SENSE_PC_SHIFT;
503   U32  page_length;
504
505   page_length = sati_mode_sense_copy_initial_data(
506                    sequence,
507                    scsi_io,
508                    offset,
509                    page_control,
510                    SCSI_MODE_PAGE_DISCONNECT_RECONNECT
511                 );
512
513   // Currently we do not override any bits in this mode page from the
514   // identify data.
515
516   return page_length;
517}
518
519/**
520 * @brief This method performs the caching mode page specific data
521 *        translation based upon the contents of the remote device IDENTIFY
522 *        DEVICE data.
523 *        For more information on the parameters passed to this method,
524 *        please reference sati_translate_command().
525 *
526 * @param[in] identify This parameter specifies the remote device's
527 *            IDENTIFY DEVICE data received as part of the IO request.
528 * @param[in] offset This parameter specifies the offset into the data
529 *            buffer where the translated data is to be written.
530 *
531 * @return This method returns the size of the mode page data that was
532 *         translated.
533 */
534U32 sati_mode_sense_caching_translate_data(
535   SATI_TRANSLATOR_SEQUENCE_T * sequence,
536   void                       * scsi_io,
537   ATA_IDENTIFY_DEVICE_DATA_T * identify,
538   U32                          offset
539)
540{
541   U8 * cdb          = sati_cb_get_cdb_address(scsi_io);
542   U8   page_control = sati_get_cdb_byte(cdb, 2) >> SCSI_MODE_SENSE_PC_SHIFT;
543   U32  page_length;
544
545   page_length = sati_mode_sense_copy_initial_data(
546                    sequence,
547                    scsi_io,
548                    offset,
549                    page_control,
550                    SCSI_MODE_PAGE_CACHING
551                 );
552
553   // If the request queried for the current values, then
554   // we need to translate the data from the IDENTIFY DEVICE request.
555   if (page_control == SCSI_MODE_SENSE_PC_CURRENT)
556   {
557      U8  value;
558
559      // Update the Write Cache Enabled (WCE) bit in the mode page data
560      // buffer based on the identify response.
561      if ((identify->command_set_enabled0 & ATA_IDENTIFY_DEVICE_WCE_ENABLE) != 0)
562      {
563         sati_get_data_byte(sequence, scsi_io, offset+2, &value);
564         value |= SCSI_MODE_PAGE_CACHE_PAGE_WCE_BIT;
565         sati_set_data_byte(sequence, scsi_io, offset+2, value);
566         //This byte has been set twice and needs to be decremented
567         sequence->number_data_bytes_set--;
568      }
569
570      // Update the Disable Read Ahead (DRA) bit in the mode page data
571      // buffer based on the identify response.
572      if ((identify->command_set_enabled0 & ATA_IDENTIFY_DEVICE_RA_ENABLE) == 0)
573      {
574         // In SATA the polarity of the bits is inverse.
575         // - SCSI = Disable Read Ahead
576         // - ATA = Read Ahead
577         sati_get_data_byte(sequence, scsi_io, offset+12, &value);
578         value |= SCSI_MODE_PAGE_CACHE_PAGE_DRA_BIT;
579         sati_set_data_byte(sequence, scsi_io, offset+12, value);
580
581         //This byte has been set twice, the first time in
582         //sati_mode_sense_copy_initial_data. number_data_bytes_set
583         //needs to be decremented
584         sequence->number_data_bytes_set--;
585      }
586   }
587
588   return page_length;
589}
590
591/**
592 * @brief This method performs the control mode page specific data
593 *        translation based upon the contents of the remote device
594 *        IDENTIFY DEVICE data.
595 *        For more information on the parameters passed to this method,
596 *        please reference sati_translate_command().
597 *
598 * @param[in] identify This parameter specifies the remote device's
599 *            IDENTIFY DEVICE data received as part of the IO request.
600 * @param[in] offset This parameter specifies the offset into the data
601 *            buffer where the translated data is to be written.
602 *
603 * @return This method returns the size of the mode page data that was
604 *         translated.
605 */
606U32 sati_mode_sense_control_translate_data(
607   SATI_TRANSLATOR_SEQUENCE_T * sequence,
608   void                       * scsi_io,
609   ATA_IDENTIFY_DEVICE_DATA_T * identify,
610   U32                          offset
611)
612{
613   U8 * cdb          = sati_cb_get_cdb_address(scsi_io);
614   U8   page_control = sati_get_cdb_byte(cdb, 2) >> SCSI_MODE_SENSE_PC_SHIFT;
615   U32  page_length;
616   U8   value;
617
618   page_length = sati_mode_sense_copy_initial_data(
619                    sequence,
620                    scsi_io,
621                    offset,
622                    page_control,
623                    SCSI_MODE_PAGE_CONTROL
624                 );
625
626   if (sequence->device->descriptor_sense_enable)
627   {
628       sati_get_data_byte(sequence, scsi_io, offset+2,
629               &value);
630
631       sati_set_data_byte(sequence, scsi_io, offset+2,
632               value | SCSI_MODE_SELECT_MODE_PAGE_D_SENSE);
633   }
634
635   return page_length;
636}
637
638/**
639 * @brief This method performs the informational exceptions control mode
640 *        page specific data translation based upon the contents of the
641 *        remote device IDENTIFY DEVICE data.
642 *        For more information on the parameters passed to this method,
643 *        please reference sati_translate_command().
644 *
645 * @param[in] identify This parameter specifies the remote device's
646 *            IDENTIFY DEVICE data received as part of the IO request.
647 * @param[in] offset This parameter specifies the offset into the data
648 *            buffer where the translated data is to be written.
649 *
650 * @return This method returns the size of the mode page data that was
651 *         translated.
652 */
653U32 sati_mode_sense_informational_excp_control_translate_data(
654   SATI_TRANSLATOR_SEQUENCE_T * sequence,
655   void                       * scsi_io,
656   ATA_IDENTIFY_DEVICE_DATA_T * identify,
657   U32                          offset
658)
659{
660   U8 * cdb          = sati_cb_get_cdb_address(scsi_io);
661   U8   page_control = sati_get_cdb_byte(cdb, 2) >> SCSI_MODE_SENSE_PC_SHIFT;
662   U32  page_length;
663
664   page_length = sati_mode_sense_copy_initial_data(
665                    sequence,
666                    scsi_io,
667                    offset,
668                    page_control,
669                    SCSI_MODE_PAGE_INFORMATIONAL_EXCP_CONTROL
670                 );
671
672   // If the request queried for the current values, then
673   // we need to translate the data from the IDENTIFY DEVICE request.
674   if (page_control == SCSI_MODE_SENSE_PC_CURRENT)
675   {
676      U8 value;
677
678      sati_get_data_byte(sequence, scsi_io, offset+2, &value);
679
680      // Determine if the SMART feature set is supported and enabled.
681      if (  (identify->command_set_supported0
682                & ATA_IDENTIFY_COMMAND_SET_SUPPORTED0_SMART_ENABLE)
683         && (identify->command_set_enabled0
684                & ATA_IDENTIFY_COMMAND_SET_ENABLED0_SMART_ENABLE) )
685      {
686         // Clear the DXCPT field since the SMART feature is supported/enabled.
687         value &= ~SCSI_MODE_PAGE_INFORMATIONAL_EXCP_DXCPT_ENABLE;
688      }
689      else
690      {
691         // Set the Disable Exception Control (DXCPT) field since the SMART
692         // feature is not supported or enabled.
693         value |= SCSI_MODE_PAGE_INFORMATIONAL_EXCP_DXCPT_ENABLE;
694      }
695
696      sati_set_data_byte(sequence, scsi_io, offset+2, value);
697
698      //This byte has been set twice, the first time in
699      //sati_mode_sense_copy_initial_data. number_data_bytes_set
700      //needs to be decremented
701      sequence->number_data_bytes_set--;
702   }
703
704   return page_length;
705}
706
707/**
708* @brief This method performs the Power Condition mode page
709*        specific data translation based upon the contents of the
710*        remote device IDENTIFY DEVICE data.
711*        For more information on the parameters passed to this method,
712*        please reference sati_translate_command().
713*
714* @param[in] identify This parameter specifies the remote device's
715*            IDENTIFY DEVICE data received as part of the IO request.
716* @param[in] offset This parameter specifies the offset into the data
717*            buffer where the translated data is to be written.
718*
719* @return This method returns the size of the mode page data that was
720*         translated.
721*/
722U32 sati_mode_sense_power_condition_translate_data(
723   SATI_TRANSLATOR_SEQUENCE_T * sequence,
724   void                       * scsi_io,
725   ATA_IDENTIFY_DEVICE_DATA_T * identify,
726   U32                          offset
727)
728{
729   U8 * cdb          = sati_cb_get_cdb_address(scsi_io);
730   U8   page_control = sati_get_cdb_byte(cdb, 2) >> SCSI_MODE_SENSE_PC_SHIFT;
731
732   U8 ata_sb_timer;
733
734   //Represents tenths of seconds
735   U32 standby_timer = 0x00000000;
736
737   U8 standby_enabled = STANDBY_TIMER_DISABLED;
738
739   if ((page_control == SCSI_MODE_SENSE_PC_CURRENT) &&
740       (identify->capabilities1 & STANDBY_TIMER_SUPPORTED))
741   {
742      standby_enabled = STANDBY_TIMER_ENABLED;
743
744      ata_sb_timer = sequence->device->ata_standby_timer;
745
746      //converting ATA timer values into SCSI timer values
747      if(ata_sb_timer <= 0xF0)
748      {
749         standby_timer = ata_sb_timer * 50;
750      }
751      else if(ata_sb_timer <= 0xFB)
752      {
753         standby_timer = ((ata_sb_timer - 240) * 18000);
754      }
755      else if(ata_sb_timer == 0xFC)
756      {
757         standby_timer = 12600;
758      }
759      else if(ata_sb_timer == 0xFD)
760      {
761         standby_timer = 432000;
762      }
763      else if(ata_sb_timer == 0xFF)
764      {
765         standby_timer = 12750;
766      }
767      else
768      {
769         standby_timer = 0xFFFFFFFF;
770      }
771   }
772
773   sati_set_data_byte(sequence, scsi_io, offset, SCSI_MODE_PAGE_POWER_CONDITION);
774   sati_set_data_byte(sequence, scsi_io, offset + 1, (SCSI_MODE_PAGE_1A_LENGTH - 2));
775   sati_set_data_byte(sequence, scsi_io, offset + 2, 0x00);
776   sati_set_data_byte(sequence, scsi_io, offset + 3, standby_enabled);
777   sati_set_data_byte(sequence, scsi_io, offset + 4, 0x00);
778   sati_set_data_byte(sequence, scsi_io, offset + 5, 0x00);
779   sati_set_data_byte(sequence, scsi_io, offset + 6, 0x00);
780   sati_set_data_byte(sequence, scsi_io, offset + 7, 0x00);
781   sati_set_data_byte(sequence, scsi_io, offset + 8, (U8) (standby_timer >> 24));
782   sati_set_data_byte(sequence, scsi_io, offset + 9, (U8) (standby_timer >> 16));
783   sati_set_data_byte(sequence, scsi_io, offset + 10, (U8) (standby_timer >> 8));
784   sati_set_data_byte(sequence, scsi_io, offset + 11, (U8) standby_timer);
785
786   return SCSI_MODE_PAGE_1A_LENGTH;
787}
788
789/**
790 * @brief This method performs the all pages mode page specific data
791 *        translation based upon the contents of the remote device
792 *        IDENTIFY DEVICE data.  The ALL PAGES mode sense request asks
793 *        for all of mode pages and sub-pages in a single page.
794 *        The mode pages are added in ascending order.
795 *        For more information on the parameters passed to this method,
796 *        please reference sati_translate_command().
797 *
798 * @param[in] identify This parameter specifies the remote device's
799 *            IDENTIFY DEVICE data received as part of the IO request.
800 * @param[in] offset This parameter specifies the offset into the data
801 *            buffer where the translated data is to be written.
802 *
803 * @return This method returns the size of the mode page data that was
804 *         translated.
805 */
806U32 sati_mode_sense_all_pages_translate_data(
807   SATI_TRANSLATOR_SEQUENCE_T * sequence,
808   void                       * scsi_io,
809   ATA_IDENTIFY_DEVICE_DATA_T * identify,
810   U32                          offset
811)
812{
813   offset += sati_mode_sense_read_write_error_translate_data(
814                sequence, scsi_io, identify, offset
815             );
816
817   offset += sati_mode_sense_disconnect_reconnect_translate_data(
818                sequence, scsi_io, identify, offset
819             );
820
821   offset += sati_mode_sense_caching_translate_data(
822                sequence, scsi_io, identify, offset
823             );
824
825   offset += sati_mode_sense_control_translate_data(
826                sequence, scsi_io, identify, offset
827             );
828
829   offset += sati_mode_sense_informational_excp_control_translate_data(
830                sequence, scsi_io, identify, offset
831             );
832
833   return offset;
834}
835
836#endif // !defined(DISABLE_SATI_MODE_SENSE)
837
838