source: mainline/uspace/drv/block/usbmast/bo_trans.c@ f1c6975

Last change on this file since f1c6975 was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 7.6 KB
RevLine 
[380e0364]1/*
2 * Copyright (c) 2011 Vojtech Horky
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 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbmast
30 * @{
31 */
32/**
33 * @file
[89d3f3c7]34 * USB mass storage bulk-only transport.
[380e0364]35 */
[58563585]36
[3e6a98c5]37#include <stdbool.h>
[380e0364]38#include <errno.h>
39#include <str_error.h>
40#include <usb/debug.h>
[7d521e24]41#include <usb/dev/request.h>
[380e0364]42
[89d3f3c7]43#include "bo_trans.h"
44#include "cmdw.h"
[7190bbc]45#include "usbmast.h"
[89d3f3c7]46
[380e0364]47#define MASTLOG(format, ...) \
[a6abe20]48 usb_log_debug2("USB cl08: " format, ##__VA_ARGS__)
[380e0364]49
[38c9505]50/** Send command via bulk-only transport.
[380e0364]51 *
[7190bbc]52 * @param mfun Mass storage function
[38c9505]53 * @param tag Command block wrapper tag (automatically compared
54 * with answer)
[de3432b]55 * @param cmd SCSI command
[38c9505]56 *
57 * @return Error code
[380e0364]58 */
[5a6cc679]59errno_t usb_massstor_cmd(usbmast_fun_t *mfun, uint32_t tag, scsi_cmd_t *cmd)
[380e0364]60{
[5a6cc679]61 errno_t rc;
[c53b58e]62
63 if (cmd->data_in && cmd->data_out)
64 return EINVAL;
65
[3e23316]66 usb_pipe_t *bulk_in_pipe = mfun->mdev->bulk_in_pipe;
67 usb_pipe_t *bulk_out_pipe = mfun->mdev->bulk_out_pipe;
[c53b58e]68
69 usb_pipe_t *dpipe = bulk_out_pipe;
70 usb_direction_t ddir = USB_DIRECTION_OUT;
71 size_t dbuf_size = cmd->data_out_size;
72
73 if (cmd->data_in) {
[de3432b]74 ddir = USB_DIRECTION_IN;
75 dbuf_size = cmd->data_in_size;
[c53b58e]76 dpipe = bulk_in_pipe;
[de3432b]77 }
[380e0364]78
79 /* Prepare CBW - command block wrapper */
80 usb_massstor_cbw_t cbw;
[e6b32a8]81 usb_massstor_cbw_prepare(&cbw, tag, dbuf_size, ddir, mfun->lun,
[de3432b]82 cmd->cdb_size, cmd->cdb);
[380e0364]83
[38c9505]84 /* Send the CBW. */
[45cae6b]85 MASTLOG("Sending CBW.\n");
[380e0364]86 rc = usb_pipe_write(bulk_out_pipe, &cbw, sizeof(cbw));
87 MASTLOG("CBW '%s' sent: %s.\n",
88 usb_debug_str_buffer((uint8_t *) &cbw, sizeof(cbw), 0),
89 str_error(rc));
[a6abe20]90 if (rc != EOK) {
[a1732929]91 usb_log_error("Bulk out write failed: %s", str_error(rc));
[45cae6b]92 return EIO;
[a6abe20]93 }
[380e0364]94
[45cae6b]95 MASTLOG("Transferring data.\n");
[c53b58e]96 if (cmd->data_in) {
97 size_t act_size;
[38c9505]98 /* Recieve data from the device. */
[c53b58e]99 rc = usb_pipe_read(dpipe, cmd->data_in, cmd->data_in_size,
100 &act_size);
[38c9505]101 MASTLOG("Received %zu bytes (%s): %s.\n", act_size,
[c53b58e]102 usb_debug_str_buffer(cmd->data_in, act_size, 0),
[38c9505]103 str_error(rc));
[c53b58e]104 }
105 if (cmd->data_out) {
[38c9505]106 /* Send data to the device. */
[c53b58e]107 rc = usb_pipe_write(dpipe, cmd->data_out, cmd->data_out_size);
108 MASTLOG("Sent %zu bytes (%s): %s.\n", cmd->data_out_size,
109 usb_debug_str_buffer(cmd->data_out, cmd->data_out_size, 0),
[38c9505]110 str_error(rc));
111 }
112
[45cae6b]113 if (rc == ESTALL) {
114 /* Clear stall condition and continue below to read CSW. */
[c53b58e]115 usb_pipe_clear_halt(
116 usb_device_get_default_pipe(mfun->mdev->usb_dev), dpipe);
[1433ecda]117 } else if (rc != EOK) {
[a6abe20]118 usb_log_error("Failed to transfer data: %s", str_error(rc));
[45cae6b]119 return EIO;
[380e0364]120 }
121
122 /* Read CSW. */
123 usb_massstor_csw_t csw;
124 size_t csw_size;
[45cae6b]125 MASTLOG("Reading CSW.\n");
[380e0364]126 rc = usb_pipe_read(bulk_in_pipe, &csw, sizeof(csw), &csw_size);
[38c9505]127 MASTLOG("CSW '%s' received (%zu bytes): %s.\n",
[380e0364]128 usb_debug_str_buffer((uint8_t *) &csw, csw_size, 0), csw_size,
129 str_error(rc));
130 if (rc != EOK) {
[a6abe20]131 usb_log_error("Failed to read CSW: %s", str_error(rc));
[45cae6b]132 return EIO;
[380e0364]133 }
[f7a55f9]134
[380e0364]135 if (csw_size != sizeof(csw)) {
[a6abe20]136 usb_log_error("Received CSW of incorrect size.");
[45cae6b]137 return EIO;
[380e0364]138 }
139
140 if (csw.dCSWTag != tag) {
[a6abe20]141 usb_log_error("Received CSW with incorrect tag. (expected: %"
[1433ecda]142 PRIX32 " received: %" PRIx32, tag, csw.dCSWTag);
[45cae6b]143 return EIO;
[380e0364]144 }
145
146 /*
147 * Determine the actual return value from the CSW.
148 */
[45cae6b]149 switch (csw.dCSWStatus) {
150 case cbs_passed:
[de3432b]151 cmd->status = CMDS_GOOD;
[45cae6b]152 break;
153 case cbs_failed:
[de3432b]154 cmd->status = CMDS_FAILED;
[a1732929]155 usb_log_error("CBS Failed.");
[45cae6b]156 break;
157 case cbs_phase_error:
[a1732929]158 usb_log_error("CBS phase error.");
[c53b58e]159 rc = EIO;
[45cae6b]160 break;
161 default:
[a1732929]162 usb_log_error("CBS other error.");
[c53b58e]163 rc = EIO;
[45cae6b]164 break;
[380e0364]165 }
166
[c53b58e]167 const size_t residue = uint32_usb2host(csw.dCSWDataResidue);
[38c9505]168 if (residue > dbuf_size) {
[a1732929]169 usb_log_error("Residue > buffer size (%zu > %zu).",
[a6abe20]170 residue, dbuf_size);
[45cae6b]171 return EIO;
[380e0364]172 }
[f7a55f9]173
174 /*
[38c9505]175 * When the device has less data to send than requested (or cannot
176 * receive moredata), it can either stall the pipe or send garbage
177 * (ignore data) and indicate that via the residue field in CSW.
178 * That means dbuf_size - residue is the authoritative size of data
179 * received (sent).
[f7a55f9]180 */
181
[c53b58e]182 if (cmd->data_in)
[de3432b]183 cmd->rcvd_size = dbuf_size - residue;
[380e0364]184
[c53b58e]185 return rc;
[380e0364]186}
187
[cf002dbf]188/** Perform bulk-only mass storage reset.
189 *
[7190bbc]190 * @param mfun Mass storage function
191 * @return Error code
[cf002dbf]192 */
[5a6cc679]193errno_t usb_massstor_reset(usbmast_dev_t *mdev)
[70c12d6]194{
[c39e9fb]195 return usb_control_request_set(
196 usb_device_get_default_pipe(mdev->usb_dev),
[70c12d6]197 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE,
[8e10ef4]198 0xFF, 0, usb_device_get_iface_number(mdev->usb_dev), NULL, 0);
[70c12d6]199}
200
[cf002dbf]201/** Perform complete reset recovery of bulk-only mass storage.
202 *
203 * Notice that no error is reported because if this fails, the error
204 * would reappear on next transaction somehow.
205 *
[7190bbc]206 * @param mfun Mass storage function
[cf002dbf]207 */
[2aceec5]208void usb_massstor_reset_recovery(usbmast_dev_t *mdev)
[70c12d6]209{
[7c3fb9b]210 /*
211 * We would ignore errors here because if this fails
[70c12d6]212 * we are doomed anyway and any following transaction would fail.
213 */
[2aceec5]214 usb_massstor_reset(mdev);
[c39e9fb]215 usb_pipe_clear_halt(usb_device_get_default_pipe(mdev->usb_dev),
[3e23316]216 mdev->bulk_in_pipe);
[c39e9fb]217 usb_pipe_clear_halt(usb_device_get_default_pipe(mdev->usb_dev),
[3e23316]218 mdev->bulk_out_pipe);
[70c12d6]219}
220
[cf002dbf]221/** Get max LUN of a mass storage device.
[5f0fe4e9]222 *
223 * @see usb_masstor_get_lun_count
[cf002dbf]224 *
225 * @warning Error from this command does not necessarily indicate malfunction
226 * of the device. Device does not need to support this request.
[5f0fe4e9]227 * You shall rather use usb_masstor_get_lun_count.
[cf002dbf]228 *
[7190bbc]229 * @param mfun Mass storage function
[d5c1051]230 * @return Maximum LUN (index, not count), or -1
[cf002dbf]231 */
[2aceec5]232int usb_massstor_get_max_lun(usbmast_dev_t *mdev)
[70c12d6]233{
234 uint8_t max_lun;
235 size_t data_recv_len;
[5a6cc679]236 errno_t rc = usb_control_request_get(
[c39e9fb]237 usb_device_get_default_pipe(mdev->usb_dev),
[70c12d6]238 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE,
[8e10ef4]239 0xFE, 0, usb_device_get_iface_number(mdev->usb_dev), &max_lun, 1,
240 &data_recv_len);
[70c12d6]241 if (rc != EOK) {
[d5c1051]242 return -1;
[70c12d6]243 }
244 if (data_recv_len != 1) {
[d5c1051]245 return -1;
[70c12d6]246 }
[d5c1051]247 return max_lun;
[70c12d6]248}
249
[5f0fe4e9]250/** Get number of LUNs supported by mass storage device.
251 *
252 * @warning This function hides any error during the request
253 * (typically that shall not be a problem).
254 *
[7190bbc]255 * @param mfun Mass storage function
256 * @return Number of LUNs
[5f0fe4e9]257 */
[2aceec5]258size_t usb_masstor_get_lun_count(usbmast_dev_t *mdev)
[5f0fe4e9]259{
[2aceec5]260 int max_lun = usb_massstor_get_max_lun(mdev);
[5f0fe4e9]261 if (max_lun < 0) {
262 max_lun = 1;
263 } else {
264 max_lun++;
265 }
266
267 return (size_t) max_lun;
268}
269
[380e0364]270/**
271 * @}
272 */
Note: See TracBrowser for help on using the repository browser.