diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 23:30:43 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 23:30:43 +0300 |
commit | d54b3538b0bfb31351d02d1669d4a978d2abfc5f (patch) | |
tree | 5ce539ecba525b30bbfb1c46c55487099264947e /drivers/scsi/scsi.c | |
parent | 5d80f8e5a9dc9c9a94d4aeaa567e219a808b8a4a (diff) | |
parent | af50bb993dfa673cf21ab812efe620d7e0c36319 (diff) | |
download | linux-d54b3538b0bfb31351d02d1669d4a978d2abfc5f.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (119 commits)
[SCSI] scsi_dh_rdac: Retry for NOT_READY check condition
[SCSI] mpt2sas: make global symbols unique
[SCSI] sd: Make revalidate less chatty
[SCSI] sd: Try READ CAPACITY 16 first for SBC-2 devices
[SCSI] sd: Refactor sd_read_capacity()
[SCSI] mpt2sas v00.100.11.15
[SCSI] mpt2sas: add MPT2SAS_MINOR(221) to miscdevice.h
[SCSI] ch: Add scsi type modalias
[SCSI] 3w-9xxx: add power management support
[SCSI] bsg: add linux/types.h include to bsg.h
[SCSI] cxgb3i: fix function descriptions
[SCSI] libiscsi: fix possbile null ptr session command cleanup
[SCSI] iscsi class: remove host no argument from session creation callout
[SCSI] libiscsi: pass session failure a session struct
[SCSI] iscsi lib: remove qdepth param from iscsi host allocation
[SCSI] iscsi lib: have lib create work queue for transmitting IO
[SCSI] iscsi class: fix lock dep warning on logout
[SCSI] libiscsi: don't cap queue depth in iscsi modules
[SCSI] iscsi_tcp: replace scsi_debug/tcp_debug logging with iscsi conn logging
[SCSI] libiscsi_tcp: replace tcp_debug/scsi_debug logging with session/conn logging
...
Diffstat (limited to 'drivers/scsi/scsi.c')
-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) EXPORT_SYMBOL(scsi_track_queue_full); /** + * scsi_vpd_inquiry - Request a device provide us with a VPD page + * @sdev: The device to ask + * @buffer: Where to put the result + * @page: Which Vital Product Data to return + * @len: The length of the buffer + * + * This is an internal helper function. You probably want to use + * scsi_get_vpd_page instead. + * + * Returns 0 on success or a negative error number. + */ +static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer, + u8 page, unsigned len) +{ + int result; + unsigned char cmd[16]; + + cmd[0] = INQUIRY; + cmd[1] = 1; /* EVPD */ + cmd[2] = page; + cmd[3] = len >> 8; + cmd[4] = len & 0xff; + cmd[5] = 0; /* Control byte */ + + /* + * I'm not convinced we need to try quite this hard to get VPD, but + * all the existing users tried this hard. + */ + result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, + len + 4, NULL, 30 * HZ, 3, NULL); + if (result) + return result; + + /* Sanity check that we got the page back that we asked for */ + if (buffer[1] != page) + return -EIO; + + return 0; +} + +/** + * scsi_get_vpd_page - Get Vital Product Data from a SCSI device + * @sdev: The device to ask + * @page: Which Vital Product Data to return + * + * SCSI devices may optionally supply Vital Product Data. Each 'page' + * of VPD is defined in the appropriate SCSI document (eg SPC, SBC). + * If the device supports this VPD page, this routine returns a pointer + * to a buffer containing the data from that page. The caller is + * responsible for calling kfree() on this pointer when it is no longer + * needed. If we cannot retrieve the VPD page this routine returns %NULL. + */ +unsigned char *scsi_get_vpd_page(struct scsi_device *sdev, u8 page) +{ + int i, result; + unsigned int len; + unsigned char *buf = kmalloc(259, GFP_KERNEL); + + if (!buf) + return NULL; + + /* Ask for all the pages supported by this device */ + result = scsi_vpd_inquiry(sdev, buf, 0, 255); + if (result) + goto fail; + + /* If the user actually wanted this page, we can skip the rest */ + if (page == 0) + return buf; + + for (i = 0; i < buf[3]; i++) + if (buf[i + 4] == page) + goto found; + /* The device claims it doesn't support the requested page */ + goto fail; + + found: + result = scsi_vpd_inquiry(sdev, buf, page, 255); + if (result) + goto fail; + + /* + * Some pages are longer than 255 bytes. The actual length of + * the page is returned in the header. + */ + len = (buf[2] << 8) | buf[3]; + if (len <= 255) + return buf; + + kfree(buf); + buf = kmalloc(len + 4, GFP_KERNEL); + result = scsi_vpd_inquiry(sdev, buf, page, len); + if (result) + goto fail; + + return buf; + + fail: + kfree(buf); + return NULL; +} +EXPORT_SYMBOL_GPL(scsi_get_vpd_page); + +/** * scsi_device_get - get an additional reference to a scsi_device * @sdev: device to get a reference to * |