xref: /freebsd/sys/cam/scsi/smp_all.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
1 /*-
2  * Copyright (c) 2010 Spectra Logic Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions, and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    substantially similar to the "NO WARRANTY" disclaimer below
13  *    ("Disclaimer") and any redistribution must be conditioned upon
14  *    including a substantially similar Disclaimer requirement for further
15  *    binary redistribution.
16  *
17  * NO WARRANTY
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGES.
29  *
30  * $Id: //depot/users/kenm/FreeBSD-test/sys/cam/scsi/smp_all.c#4 $
31  */
32 
33 /*
34  * Serial Management Protocol helper functions.
35  */
36 
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 #include <sys/param.h>
41 #include <sys/types.h>
42 #ifdef _KERNEL
43 #include <sys/systm.h>
44 #include <sys/libkern.h>
45 #include <sys/kernel.h>
46 #else /* _KERNEL */
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <inttypes.h>
52 #endif /* _KERNEL */
53 
54 #include <cam/cam.h>
55 #include <cam/cam_ccb.h>
56 #include <cam/cam_xpt.h>
57 #include <cam/scsi/smp_all.h>
58 #include <sys/sbuf.h>
59 
60 #ifndef _KERNEL
61 #include <camlib.h>
62 #endif
63 
64 static char *smp_yesno(int val);
65 
66 static char *
67 smp_yesno(int val)
68 {
69 	char *str;
70 
71 	if (val)
72 		str = "Yes";
73 	else
74 		str = "No";
75 
76 	return (str);
77 }
78 
79 struct smp_error_table_entry {
80 	uint8_t	function_result;
81 	const char *desc;
82 };
83 
84 /* List current as of SPL Revision 7 */
85 static struct smp_error_table_entry smp_error_table[] = {
86 	{SMP_FR_ACCEPTED, "SMP Function Accepted"},
87 	{SMP_FR_UNKNOWN_FUNC, "Unknown SMP Function"},
88 	{SMP_FR_FUNCTION_FAILED, "SMP Function Failed"},
89 	{SMP_FR_INVALID_REQ_FRAME_LEN, "Invalid Request Frame Length"},
90 	{SMP_FR_INVALID_EXP_CHG_CNT, "Invalid Expander Change Count"},
91 	{SMP_FR_BUSY, "Busy"},
92 	{SMP_FR_INCOMPLETE_DESC_LIST, "Incomplete Descriptor List"},
93 	{SMP_FR_PHY_DOES_NOT_EXIST, "Phy Does Not Exist"},
94 	{SMP_FR_INDEX_DOES_NOT_EXIST, "Index Does Not Exist"},
95 	{SMP_FR_PHY_DOES_NOT_SUP_SATA, "Phy Does Not Support SATA"},
96 	{SMP_FR_UNKNOWN_PHY_OP, "Unknown Phy Operation"},
97 	{SMP_FR_UNKNOWN_PHY_TEST_FUNC, "Unknown Phy Test Function"},
98 	{SMP_FR_PHY_TEST_FUNC_INPROG, "Phy Test Function In Progress"},
99 	{SMP_FR_PHY_VACANT, "Phy Vacant"},
100 	{SMP_FR_UNKNOWN_PHY_EVENT_SRC, "Unknown Phy Event Source"},
101 	{SMP_FR_UNKNOWN_DESC_TYPE, "Unknown Descriptor Type"},
102 	{SMP_FR_UNKNOWN_PHY_FILTER, "Unknown Phy Filter"},
103 	{SMP_FR_AFFILIATION_VIOLATION, "Affiliation Violation"},
104 	{SMP_FR_SMP_ZONE_VIOLATION, "SMP Zone Violation"},
105 	{SMP_FR_NO_MGMT_ACCESS_RIGHTS, "No Management Access Rights"},
106 	{SMP_FR_UNKNOWN_ED_ZONING_VAL, "Unknown Enable Disable Zoning Value"},
107 	{SMP_FR_ZONE_LOCK_VIOLATION, "Zone Lock Violation"},
108 	{SMP_FR_NOT_ACTIVATED, "Not Activated"},
109 	{SMP_FR_ZG_OUT_OF_RANGE, "Zone Group Out of Range"},
110 	{SMP_FR_NO_PHYS_PRESENCE, "No Physical Presence"},
111 	{SMP_FR_SAVING_NOT_SUP, "Saving Not Supported"},
112 	{SMP_FR_SRC_ZONE_DNE, "Source Zone Group Does Not Exist"},
113 	{SMP_FR_DISABLED_PWD_NOT_SUP, "Disabled Password Not Supported"}
114 };
115 
116 const char *
117 smp_error_desc(int function_result)
118 {
119 	int i;
120 
121 	for (i = 0; i < (sizeof(smp_error_table)/sizeof(smp_error_table[0]));
122 	     i++){
123 		if (function_result == smp_error_table[i].function_result)
124 			return (smp_error_table[i].desc);
125 	}
126 	return ("Reserved Function Result");
127 }
128 
129 /* List current as of SPL Revision 7 */
130 struct smp_cmd_table_entry {
131 	uint8_t	cmd_num;
132 	const char *desc;
133 } smp_cmd_table[] = {
134 	{SMP_FUNC_REPORT_GENERAL, "REPORT GENERAL"},
135 	{SMP_FUNC_REPORT_MANUF_INFO, "REPORT MANUFACTURER INFORMATION"},
136 	{SMP_FUNC_REPORT_SC_STATUS, "REPORT SELF-CONFIGURATION STATUS"},
137 	{SMP_FUNC_REPORT_ZONE_PERM_TBL, "REPORT ZONE PERMISSION TABLE"},
138 	{SMP_FUNC_REPORT_BROADCAST, "REPORT BROADCAST"},
139 	{SMP_FUNC_DISCOVER, "DISCOVER"},
140 	{SMP_FUNC_REPORT_PHY_ERR_LOG, "REPORT PHY ERROR LOG"},
141 	{SMP_FUNC_REPORT_PHY_SATA, "REPORT PHY SATA"},
142 	{SMP_FUNC_REPORT_ROUTE_INFO, "REPORT ROUTE INFORMATION"},
143 	{SMP_FUNC_REPORT_PHY_EVENT, "REPORT PHY EVENT"},
144 	{SMP_FUNC_DISCOVER_LIST, "DISCOVER LIST"},
145 	{SMP_FUNC_REPORT_PHY_EVENT_LIST, "REPORT PHY EVENT LIST"},
146 	{SMP_FUNC_REPORT_EXP_RTL, "REPORT EXPANDER ROUTE TABLE LIST"},
147 	{SMP_FUNC_CONFIG_GENERAL, "CONFIGURE GENERAL"},
148 	{SMP_FUNC_ENABLE_DISABLE_ZONING, "ENABLE DISABLE ZONING"},
149 	{SMP_FUNC_ZONED_BROADCAST, "ZONED BROADCAST"},
150 	{SMP_FUNC_ZONE_LOCK, "ZONE LOCK"},
151 	{SMP_FUNC_ZONE_ACTIVATE, "ZONE ACTIVATE"},
152 	{SMP_FUNC_ZONE_UNLOCK, "ZONE UNLOCK"},
153 	{SMP_FUNC_CONFIG_ZM_PWD, "CONFIGURE ZONE MANAGER PASSWORD"},
154 	{SMP_FUNC_CONFIG_ZONE_PHY_INFO, "CONFIGURE ZONE PHY INFORMATION"},
155 	{SMP_FUNC_CONFIG_ZONE_PERM_TBL, "CONFIGURE ZONE PERMISSION TABLE"},
156 	{SMP_FUNC_CONFIG_ROUTE_INFO, "CONFIGURE ROUTE INFORMATION"},
157 	{SMP_FUNC_PHY_CONTROL, "PHY CONTROL"},
158 	{SMP_FUNC_PHY_TEST_FUNC, "PHY TEST FUNCTION"},
159 	{SMP_FUNC_CONFIG_PHY_EVENT, "CONFIGURE PHY EVENT"}
160 };
161 
162 const char *
163 smp_command_desc(uint8_t cmd_num)
164 {
165 	int i;
166 
167 	for (i = 0; i < (sizeof(smp_cmd_table)/sizeof(smp_cmd_table[0])) &&
168 	     smp_cmd_table[i].cmd_num <= cmd_num; i++) {
169 		if (cmd_num == smp_cmd_table[i].cmd_num)
170 			return (smp_cmd_table[i].desc);
171 	}
172 
173 	/*
174 	 * 0x40 to 0x7f and 0xc0 to 0xff are the vendor specific SMP
175 	 * command ranges.
176 	 */
177 	if (((cmd_num >= 0x40) && (cmd_num <= 0x7f))
178 	 || (cmd_num >= 0xc0)) {
179 		return ("Vendor Specific SMP Command");
180 	} else {
181 		return ("Unknown SMP Command");
182 	}
183 }
184 
185 /*
186  * Decode a SMP request buffer into a string of hexadecimal numbers.
187  *
188  * smp_request:    SMP request
189  * request_len:    length of the SMP request buffer, may be reduced if the
190  *                 caller only wants part of the buffer printed
191  * sb:             sbuf(9) buffer
192  * line_prefix:    prefix for new lines, or an empty string ("")
193  * first_line_len: length left on first line
194  * line_len:       total length of subsequent lines, 0 for no additional lines
195  *                 if there are no additional lines, first line will get ...
196  *                 at the end if there is additional data
197  */
198 void
199 smp_command_decode(uint8_t *smp_request, int request_len, struct sbuf *sb,
200 		   char *line_prefix, int first_line_len, int line_len)
201 {
202 	int i, cur_len;
203 
204 	for (i = 0, cur_len = first_line_len; i < request_len; i++) {
205 		/*
206 		 * Each byte takes 3 characters.  As soon as we go less
207 		 * than 6 (meaning we have at least 3 and at most 5
208 		 * characters left), check to see whether the subsequent
209 		 * line length (line_len) is long enough to bother with.
210 		 * If the user set it to 0, or some other length that isn't
211 		 * enough to hold at least the prefix and one byte, put ...
212 		 * on the first line to indicate that there is more data
213 		 * and bail out.
214 		 */
215 		if ((cur_len < 6)
216 		 && (line_len < (strlen(line_prefix) + 3))) {
217 			sbuf_printf(sb, "...");
218 			return;
219 		}
220 		if (cur_len < 3) {
221 			sbuf_printf(sb, "\n%s", line_prefix);
222 			cur_len = line_len - strlen(line_prefix);
223 		}
224 		sbuf_printf(sb, "%02x ", smp_request[i]);
225 		cur_len = cur_len - 3;
226 	}
227 }
228 
229 void
230 smp_command_sbuf(struct ccb_smpio *smpio, struct sbuf *sb,
231 		 char *line_prefix, int first_line_len, int line_len)
232 {
233 	sbuf_printf(sb, "%s. ", smp_command_desc(smpio->smp_request[1]));
234 
235 	/*
236 	 * Acccount for the command description and the period and space
237 	 * after the command description.
238 	 */
239 	first_line_len -= strlen(smp_command_desc(smpio->smp_request[1])) + 2;
240 
241 	smp_command_decode(smpio->smp_request, smpio->smp_request_len, sb,
242 			   line_prefix, first_line_len, line_len);
243 }
244 
245 /*
246  * Print SMP error output.  For userland commands, we need the cam_device
247  * structure so we can get the path information from the CCB.
248  */
249 #ifdef _KERNEL
250 void
251 smp_error_sbuf(struct ccb_smpio *smpio, struct sbuf *sb)
252 #else /* !_KERNEL*/
253 void
254 smp_error_sbuf(struct cam_device *device, struct ccb_smpio *smpio,
255 	       struct sbuf *sb)
256 #endif /* _KERNEL/!_KERNEL */
257 {
258 	char path_str[64];
259 
260 #ifdef _KERNEL
261 	xpt_path_string(smpio->ccb_h.path, path_str, sizeof(path_str));
262 #else
263 	cam_path_string(device, path_str, sizeof(path_str));
264 #endif
265 	smp_command_sbuf(smpio, sb, path_str, 80 - strlen(path_str), 80);
266 	sbuf_printf(sb, "\n");
267 
268 	sbuf_cat(sb, path_str);
269 	sbuf_printf(sb, "SMP Error: %s (0x%x)\n",
270 		    smp_error_desc(smpio->smp_response[2]),
271 		    smpio->smp_response[2]);
272 }
273 
274 /*
275  * Decode the SMP REPORT GENERAL response.  The format is current as of SPL
276  * Revision 7, but the parsing should be backward compatible for older
277  * versions of the spec.
278  */
279 void
280 smp_report_general_sbuf(struct smp_report_general_response *response,
281 			int response_len, struct sbuf *sb)
282 {
283 	sbuf_printf(sb, "Report General\n");
284 	sbuf_printf(sb, "Response Length: %d words (%d bytes)\n",
285 		    response->response_len,
286 		    response->response_len * SMP_WORD_LEN);
287 	sbuf_printf(sb, "Expander Change Count: %d\n",
288 		    scsi_2btoul(response->expander_change_count));
289 	sbuf_printf(sb, "Expander Route Indexes: %d\n",
290 		    scsi_2btoul(response->expander_route_indexes));
291 	sbuf_printf(sb, "Long Response: %s\n",
292 		    smp_yesno(response->long_response &
293 			      SMP_RG_LONG_RESPONSE));
294 	sbuf_printf(sb, "Number of Phys: %d\n", response->num_phys);
295 	sbuf_printf(sb, "Table to Table Supported: %s\n",
296 		    smp_yesno(response->config_bits0 &
297 		    SMP_RG_TABLE_TO_TABLE_SUP));
298 	sbuf_printf(sb, "Zone Configuring: %s\n",
299 		    smp_yesno(response->config_bits0 &
300 		    SMP_RG_ZONE_CONFIGURING));
301 	sbuf_printf(sb, "Self Configuring: %s\n",
302 		    smp_yesno(response->config_bits0 &
303 		    SMP_RG_SELF_CONFIGURING));
304 	sbuf_printf(sb, "STP Continue AWT: %s\n",
305 		    smp_yesno(response->config_bits0 &
306 		    SMP_RG_STP_CONTINUE_AWT));
307 	sbuf_printf(sb, "Open Reject Retry Supported: %s\n",
308 		    smp_yesno(response->config_bits0 &
309 		    SMP_RG_OPEN_REJECT_RETRY_SUP));
310 	sbuf_printf(sb, "Configures Others: %s\n",
311 		    smp_yesno(response->config_bits0 &
312 		    SMP_RG_CONFIGURES_OTHERS));
313 	sbuf_printf(sb, "Configuring: %s\n",
314 		    smp_yesno(response->config_bits0 &
315 		    SMP_RG_CONFIGURING));
316 	sbuf_printf(sb, "Externally Configurable Route Table: %s\n",
317 		    smp_yesno(response->config_bits0 &
318 		    SMP_RG_CONFIGURING));
319 	sbuf_printf(sb, "Enclosure Logical Identifier: 0x%016jx\n",
320 		    (uintmax_t)scsi_8btou64(response->encl_logical_id));
321 
322 	/*
323 	 * If the response->response_len is 0, then we don't have the
324 	 * extended information.  Also, if the user didn't allocate enough
325 	 * space for the full request, don't try to parse it.
326 	 */
327 	if ((response->response_len == 0)
328 	 || (response_len < (sizeof(struct smp_report_general_response) -
329 	     sizeof(response->crc))))
330 		return;
331 
332 	sbuf_printf(sb, "STP Bus Inactivity Time Limit: %d\n",
333 		    scsi_2btoul(response->stp_bus_inact_time_limit));
334 	sbuf_printf(sb, "STP Maximum Connect Time Limit: %d\n",
335 		    scsi_2btoul(response->stp_max_conn_time_limit));
336 	sbuf_printf(sb, "STP SMP I_T Nexus Loss Time: %d\n",
337 		    scsi_2btoul(response->stp_smp_it_nexus_loss_time));
338 
339 	sbuf_printf(sb, "Number of Zone Groups: %d\n",
340 		    (response->config_bits1 & SMP_RG_NUM_ZONE_GROUPS_MASK) >>
341 		    SMP_RG_NUM_ZONE_GROUPS_SHIFT);
342 	sbuf_printf(sb, "Zone Locked: %s\n",
343 		    smp_yesno(response->config_bits1 & SMP_RG_ZONE_LOCKED));
344 	sbuf_printf(sb, "Physical Presence Supported: %s\n",
345 		    smp_yesno(response->config_bits1 & SMP_RG_PP_SUPPORTED));
346 	sbuf_printf(sb, "Physical Presence Asserted: %s\n",
347 		    smp_yesno(response->config_bits1 & SMP_RG_PP_ASSERTED));
348 	sbuf_printf(sb, "Zoning Supported: %s\n",
349 		    smp_yesno(response->config_bits1 &
350 			      SMP_RG_ZONING_SUPPORTED));
351 	sbuf_printf(sb, "Zoning Enabled: %s\n",
352 		    smp_yesno(response->config_bits1 & SMP_RG_ZONING_ENABLED));
353 
354 	sbuf_printf(sb, "Saving: %s\n",
355 		    smp_yesno(response->config_bits2 & SMP_RG_SAVING));
356 	sbuf_printf(sb, "Saving Zone Manager Password Supported: %s\n",
357 		    smp_yesno(response->config_bits2 &
358 			      SMP_RG_SAVING_ZM_PWD_SUP));
359 	sbuf_printf(sb, "Saving Zone Phy Information Supported: %s\n",
360 		    smp_yesno(response->config_bits2 &
361 			      SMP_RG_SAVING_PHY_INFO_SUP));
362 	sbuf_printf(sb, "Saving Zone Permission Table Supported: %s\n",
363 		    smp_yesno(response->config_bits2 &
364 			      SMP_RG_SAVING_ZPERM_TAB_SUP));
365 	sbuf_printf(sb, "Saving Zoning Enabled Supported: %s\n",
366 		    smp_yesno(response->config_bits2 &
367 			      SMP_RG_SAVING_ZENABLED_SUP));
368 
369 	sbuf_printf(sb, "Maximum Number of Routed SAS Addresses: %d\n",
370 		    scsi_2btoul(response->max_num_routed_addrs));
371 
372 	sbuf_printf(sb, "Active Zone Manager SAS Address: 0x%016jx\n",
373 		    scsi_8btou64(response->active_zm_address));
374 
375 	sbuf_printf(sb, "Zone Inactivity Time Limit: %d\n",
376 		    scsi_2btoul(response->zone_lock_inact_time_limit));
377 
378 	sbuf_printf(sb, "First Enclosure Connector Element Index: %d\n",
379 		    response->first_encl_conn_el_index);
380 
381 	sbuf_printf(sb, "Number of Enclosure Connector Element Indexes: %d\n",
382 		    response->num_encl_conn_el_indexes);
383 
384 	sbuf_printf(sb, "Reduced Functionality: %s\n",
385 		    smp_yesno(response->reduced_functionality &
386 			      SMP_RG_REDUCED_FUNCTIONALITY));
387 
388 	sbuf_printf(sb, "Time to Reduced Functionality: %d\n",
389 		    response->time_to_reduced_func);
390 	sbuf_printf(sb, "Initial Time to Reduced Functionality: %d\n",
391 		    response->initial_time_to_reduced_func);
392 	sbuf_printf(sb, "Maximum Reduced Functionality Time: %d\n",
393 		    response->max_reduced_func_time);
394 
395 	sbuf_printf(sb, "Last Self-Configuration Status Descriptor Index: %d\n",
396 		    scsi_2btoul(response->last_sc_stat_desc_index));
397 
398 	sbuf_printf(sb, "Maximum Number of Storated Self-Configuration "
399 		    "Status Descriptors: %d\n",
400 		    scsi_2btoul(response->max_sc_stat_descs));
401 
402 	sbuf_printf(sb, "Last Phy Event List Descriptor Index: %d\n",
403 		    scsi_2btoul(response->last_phy_evl_desc_index));
404 
405 	sbuf_printf(sb, "Maximum Number of Stored Phy Event List "
406 		    "Descriptors: %d\n",
407 		    scsi_2btoul(response->max_stored_pel_descs));
408 
409 	sbuf_printf(sb, "STP Reject to Open Limit: %d\n",
410 		    scsi_2btoul(response->stp_reject_to_open_limit));
411 }
412 
413 /*
414  * Decode the SMP REPORT MANUFACTURER INFORMATION response.  The format is
415  * current as of SPL Revision 7, but the parsing should be backward
416  * compatible for older versions of the spec.
417  */
418 void
419 smp_report_manuf_info_sbuf(struct smp_report_manuf_info_response *response,
420 			   int response_len, struct sbuf *sb)
421 {
422 	char vendor[16], product[48], revision[16];
423 	char comp_vendor[16];
424 
425 	sbuf_printf(sb, "Report Manufacturer Information\n");
426 	sbuf_printf(sb, "Expander Change count: %d\n",
427 		    scsi_2btoul(response->expander_change_count));
428 	sbuf_printf(sb, "SAS 1.1 Format: %s\n",
429 		    smp_yesno(response->sas_11_format & SMP_RMI_SAS11_FORMAT));
430 	cam_strvis(vendor, response->vendor, sizeof(response->vendor),
431 		   sizeof(vendor));
432 	cam_strvis(product, response->product, sizeof(response->product),
433 		   sizeof(product));
434 	cam_strvis(revision, response->revision, sizeof(response->revision),
435 		   sizeof(revision));
436 	sbuf_printf(sb, "<%s %s %s>\n", vendor, product, revision);
437 
438 	if ((response->sas_11_format & SMP_RMI_SAS11_FORMAT) == 0) {
439 		uint8_t *curbyte;
440 		int line_start, line_cursor;
441 
442 		sbuf_printf(sb, "Vendor Specific Data:\n");
443 
444 		/*
445 		 * Print out the bytes roughly in the style of hd(1), but
446 		 * without the extra ASCII decoding.  Hexadecimal line
447 		 * numbers on the left, and 16 bytes per line, with an
448 		 * extra space after the first 8 bytes.
449 		 *
450 		 * It would be nice if this sort of thing were available
451 		 * in a library routine.
452 		 */
453 		for (curbyte = (uint8_t *)&response->comp_vendor, line_start= 1,
454 		     line_cursor = 0; curbyte < (uint8_t *)&response->crc;
455 		     curbyte++, line_cursor++) {
456 			if (line_start != 0) {
457 				sbuf_printf(sb, "%08lx  ",
458 					    (unsigned long)(curbyte -
459 					    (uint8_t *)response));
460 				line_start = 0;
461 				line_cursor = 0;
462 			}
463 			sbuf_printf(sb, "%02x", *curbyte);
464 
465 			if (line_cursor == 15) {
466 				sbuf_printf(sb, "\n");
467 				line_start = 1;
468 			} else
469 				sbuf_printf(sb, " %s", (line_cursor == 7) ?
470 					    " " : "");
471 		}
472 		if (line_cursor != 16)
473 			sbuf_printf(sb, "\n");
474 		return;
475 	}
476 
477 	cam_strvis(comp_vendor, response->comp_vendor,
478 		   sizeof(response->comp_vendor), sizeof(comp_vendor));
479 	sbuf_printf(sb, "Component Vendor: %s\n", comp_vendor);
480 	sbuf_printf(sb, "Component ID: %#x\n", scsi_2btoul(response->comp_id));
481 	sbuf_printf(sb, "Component Revision: %#x\n", response->comp_revision);
482 	sbuf_printf(sb, "Vendor Specific: 0x%016jx\n",
483 		    (uintmax_t)scsi_8btou64(response->vendor_specific));
484 }
485 
486 /*
487  * Compose a SMP REPORT GENERAL request and put it into a CCB.  This is
488  * current as of SPL Revision 7.
489  */
490 void
491 smp_report_general(struct ccb_smpio *smpio, uint32_t retries,
492 		   void (*cbfcnp)(struct cam_periph *, union ccb *),
493 		   struct smp_report_general_request *request, int request_len,
494 		   uint8_t *response, int response_len, int long_response,
495 		   uint32_t timeout)
496 {
497 	cam_fill_smpio(smpio,
498 		       retries,
499 		       cbfcnp,
500 		       /*flags*/CAM_DIR_BOTH,
501 		       (uint8_t *)request,
502 		       request_len - SMP_CRC_LEN,
503 		       response,
504 		       response_len,
505 		       timeout);
506 
507 	bzero(request, sizeof(*request));
508 
509 	request->frame_type = SMP_FRAME_TYPE_REQUEST;
510 	request->function = SMP_FUNC_REPORT_GENERAL;
511 	request->response_len = long_response ? SMP_RG_RESPONSE_LEN : 0;
512 	request->request_len = 0;
513 }
514 
515 /*
516  * Compose a SMP DISCOVER request and put it into a CCB.  This is current
517  * as of SPL Revision 7.
518  */
519 void
520 smp_discover(struct ccb_smpio *smpio, uint32_t retries,
521 	     void (*cbfcnp)(struct cam_periph *, union ccb *),
522 	     struct smp_discover_request *request, int request_len,
523 	     uint8_t *response, int response_len, int long_response,
524 	     int ignore_zone_group, int phy, uint32_t timeout)
525 {
526 	cam_fill_smpio(smpio,
527 		       retries,
528 		       cbfcnp,
529 		       /*flags*/CAM_DIR_BOTH,
530 		       (uint8_t *)request,
531 		       request_len - SMP_CRC_LEN,
532 		       response,
533 		       response_len,
534 		       timeout);
535 
536 	bzero(request, sizeof(*request));
537 	request->frame_type = SMP_FRAME_TYPE_REQUEST;
538 	request->function = SMP_FUNC_DISCOVER;
539 	request->response_len = long_response ? SMP_DIS_RESPONSE_LEN : 0;
540 	request->request_len = long_response ? SMP_DIS_REQUEST_LEN : 0;
541 	if (ignore_zone_group != 0)
542 		request->ignore_zone_group |= SMP_DIS_IGNORE_ZONE_GROUP;
543 	request->phy = phy;
544 }
545 
546 /*
547  * Compose a SMP REPORT MANUFACTURER INFORMATION request and put it into a
548  * CCB.  This is current as of SPL Revision 7.
549  */
550 void
551 smp_report_manuf_info(struct ccb_smpio *smpio, uint32_t retries,
552 		      void (*cbfcnp)(struct cam_periph *, union ccb *),
553 		      struct smp_report_manuf_info_request *request,
554 		      int request_len, uint8_t *response, int response_len,
555 		      int long_response, uint32_t timeout)
556 {
557 	cam_fill_smpio(smpio,
558 		       retries,
559 		       cbfcnp,
560 		       /*flags*/CAM_DIR_BOTH,
561 		       (uint8_t *)request,
562 		       request_len - SMP_CRC_LEN,
563 		       response,
564 		       response_len,
565 		       timeout);
566 
567 	bzero(request, sizeof(*request));
568 
569 	request->frame_type = SMP_FRAME_TYPE_REQUEST;
570 	request->function = SMP_FUNC_REPORT_MANUF_INFO;
571 	request->response_len = long_response ? SMP_RMI_RESPONSE_LEN : 0;
572 	request->request_len = long_response ? SMP_RMI_REQUEST_LEN : 0;
573 }
574 
575 /*
576  * Compose a SMP PHY CONTROL request and put it into a CCB.  This is
577  * current as of SPL Revision 7.
578  */
579 void
580 smp_phy_control(struct ccb_smpio *smpio, uint32_t retries,
581 		void (*cbfcnp)(struct cam_periph *, union ccb *),
582 		struct smp_phy_control_request *request, int request_len,
583 		uint8_t *response, int response_len, int long_response,
584 		uint32_t expected_exp_change_count, int phy, int phy_op,
585 		int update_pp_timeout_val, uint64_t attached_device_name,
586 		int prog_min_prl, int prog_max_prl, int slumber_partial,
587 		int pp_timeout_value, uint32_t timeout)
588 {
589 	cam_fill_smpio(smpio,
590 		       retries,
591 		       cbfcnp,
592 		       /*flags*/CAM_DIR_BOTH,
593 		       (uint8_t *)request,
594 		       request_len - SMP_CRC_LEN,
595 		       response,
596 		       response_len,
597 		       timeout);
598 
599 	bzero(request, sizeof(*request));
600 
601 	request->frame_type = SMP_FRAME_TYPE_REQUEST;
602 	request->function = SMP_FUNC_PHY_CONTROL;
603 	request->response_len = long_response ? SMP_PC_RESPONSE_LEN : 0;
604 	request->request_len = long_response ? SMP_PC_REQUEST_LEN : 0;
605 	scsi_ulto2b(expected_exp_change_count, request->expected_exp_chg_cnt);
606 	request->phy = phy;
607 	request->phy_operation = phy_op;
608 
609 	if (update_pp_timeout_val != 0)
610 		request->update_pp_timeout |= SMP_PC_UPDATE_PP_TIMEOUT;
611 
612 	scsi_u64to8b(attached_device_name, request->attached_device_name);
613 	request->prog_min_phys_link_rate = (prog_min_prl <<
614 		SMP_PC_PROG_MIN_PL_RATE_SHIFT) & SMP_PC_PROG_MIN_PL_RATE_MASK;
615 	request->prog_max_phys_link_rate = (prog_max_prl <<
616 		SMP_PC_PROG_MAX_PL_RATE_SHIFT) & SMP_PC_PROG_MAX_PL_RATE_MASK;
617 	request->config_bits0 = slumber_partial;
618 	request->pp_timeout_value = pp_timeout_value;
619 }
620 
621