diff options
author | Matthew Wilcox <matthew@wil.cx> | 2008-12-31 13:12:46 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-03-12 13:57:54 -0400 |
commit | 881a256d84e658d14ca1c162fe56e9cbbb1cdd49 (patch) | |
tree | 76f77b27d87f823b6a2429a04ff925bee6c3107d /drivers/scsi | |
parent | 5fa0ae19822d60307059ee64b80ba9e5effdce58 (diff) |
[SCSI] Add VPD helper
Based on prior work by Martin Petersen and James Bottomley, this patch
adds a generic helper for retrieving VPD pages from SCSI devices.
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/scsi.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index cbcd3f681b62..a2ef03243a2c 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c | |||
@@ -967,6 +967,110 @@ int scsi_track_queue_full(struct scsi_device *sdev, int depth) | |||
967 | EXPORT_SYMBOL(scsi_track_queue_full); | 967 | EXPORT_SYMBOL(scsi_track_queue_full); |
968 | 968 | ||
969 | /** | 969 | /** |
970 | * scsi_vpd_inquiry - Request a device provide us with a VPD page | ||
971 | * @sdev: The device to ask | ||
972 | * @buffer: Where to put the result | ||
973 | * @page: Which Vital Product Data to return | ||
974 | * @len: The length of the buffer | ||
975 | * | ||
976 | * This is an internal helper function. You probably want to use | ||
977 | * scsi_get_vpd_page instead. | ||
978 | * | ||
979 | * Returns 0 on success or a negative error number. | ||
980 | */ | ||
981 | static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer, | ||
982 | u8 page, unsigned len) | ||
983 | { | ||
984 | int result; | ||
985 | unsigned char cmd[16]; | ||
986 | |||
987 | cmd[0] = INQUIRY; | ||
988 | cmd[1] = 1; /* EVPD */ | ||
989 | cmd[2] = page; | ||
990 | cmd[3] = len >> 8; | ||
991 | cmd[4] = len & 0xff; | ||
992 | cmd[5] = 0; /* Control byte */ | ||
993 | |||
994 | /* | ||
995 | * I'm not convinced we need to try quite this hard to get VPD, but | ||
996 | * all the existing users tried this hard. | ||
997 | */ | ||
998 | result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, | ||
999 | len + 4, NULL, 30 * HZ, 3, NULL); | ||
1000 | if (result) | ||
1001 | return result; | ||
1002 | |||
1003 | /* Sanity check that we got the page back that we asked for */ | ||
1004 | if (buffer[1] != page) | ||
1005 | return -EIO; | ||
1006 | |||
1007 | return 0; | ||
1008 | } | ||
1009 | |||
1010 | /** | ||
1011 | * scsi_get_vpd_page - Get Vital Product Data from a SCSI device | ||
1012 | * @sdev: The device to ask | ||
1013 | * @page: Which Vital Product Data to return | ||
1014 | * | ||
1015 | * SCSI devices may optionally supply Vital Product Data. Each 'page' | ||
1016 | * of VPD is defined in the appropriate SCSI document (eg SPC, SBC). | ||
1017 | * If the device supports this VPD page, this routine returns a pointer | ||
1018 | * to a buffer containing the data from that page. The caller is | ||
1019 | * responsible for calling kfree() on this pointer when it is no longer | ||
1020 | * needed. If we cannot retrieve the VPD page this routine returns %NULL. | ||
1021 | */ | ||
1022 | unsigned char *scsi_get_vpd_page(struct scsi_device *sdev, u8 page) | ||
1023 | { | ||
1024 | int i, result; | ||
1025 | unsigned int len; | ||
1026 | unsigned char *buf = kmalloc(259, GFP_KERNEL); | ||
1027 | |||
1028 | if (!buf) | ||
1029 | return NULL; | ||
1030 | |||
1031 | /* Ask for all the pages supported by this device */ | ||
1032 | result = scsi_vpd_inquiry(sdev, buf, 0, 255); | ||
1033 | if (result) | ||
1034 | goto fail; | ||
1035 | |||
1036 | /* If the user actually wanted this page, we can skip the rest */ | ||
1037 | if (page == 0) | ||
1038 | return buf; | ||
1039 | |||
1040 | for (i = 0; i < buf[3]; i++) | ||
1041 | if (buf[i + 4] == page) | ||
1042 | goto found; | ||
1043 | /* The device claims it doesn't support the requested page */ | ||
1044 | goto fail; | ||
1045 | |||
1046 | found: | ||
1047 | result = scsi_vpd_inquiry(sdev, buf, page, 255); | ||
1048 | if (result) | ||
1049 | goto fail; | ||
1050 | |||
1051 | /* | ||
1052 | * Some pages are longer than 255 bytes. The actual length of | ||
1053 | * the page is returned in the header. | ||
1054 | */ | ||
1055 | len = (buf[2] << 8) | buf[3]; | ||
1056 | if (len <= 255) | ||
1057 | return buf; | ||
1058 | |||
1059 | kfree(buf); | ||
1060 | buf = kmalloc(len + 4, GFP_KERNEL); | ||
1061 | result = scsi_vpd_inquiry(sdev, buf, page, len); | ||
1062 | if (result) | ||
1063 | goto fail; | ||
1064 | |||
1065 | return buf; | ||
1066 | |||
1067 | fail: | ||
1068 | kfree(buf); | ||
1069 | return NULL; | ||
1070 | } | ||
1071 | EXPORT_SYMBOL_GPL(scsi_get_vpd_page); | ||
1072 | |||
1073 | /** | ||
970 | * scsi_device_get - get an additional reference to a scsi_device | 1074 | * scsi_device_get - get an additional reference to a scsi_device |
971 | * @sdev: device to get a reference to | 1075 | * @sdev: device to get a reference to |
972 | * | 1076 | * |