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

Last change on this file since d7f7a4a was d7f7a4a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 years ago

Replace some license headers with SPDX identifier

Headers are replaced using tools/transorm-copyright.sh only
when it can be matched verbatim with the license header used
throughout most of the codebase.

  • Property mode set to 100644
File size: 6.2 KB
Line 
1/*
2 * SPDX-FileCopyrightText: 2011 Vojtech Horky
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7/** @addtogroup drvusbmast
8 * @{
9 */
10/**
11 * @file
12 * USB mass storage bulk-only transport.
13 */
14
15#include <stdbool.h>
16#include <errno.h>
17#include <str_error.h>
18#include <usb/debug.h>
19#include <usb/dev/request.h>
20
21#include "bo_trans.h"
22#include "cmdw.h"
23#include "usbmast.h"
24
25#define MASTLOG(format, ...) \
26 usb_log_debug2("USB cl08: " format, ##__VA_ARGS__)
27
28/** Send command via bulk-only transport.
29 *
30 * @param mfun Mass storage function
31 * @param tag Command block wrapper tag (automatically compared
32 * with answer)
33 * @param cmd SCSI command
34 *
35 * @return Error code
36 */
37errno_t usb_massstor_cmd(usbmast_fun_t *mfun, uint32_t tag, scsi_cmd_t *cmd)
38{
39 errno_t rc;
40
41 if (cmd->data_in && cmd->data_out)
42 return EINVAL;
43
44 usb_pipe_t *bulk_in_pipe = mfun->mdev->bulk_in_pipe;
45 usb_pipe_t *bulk_out_pipe = mfun->mdev->bulk_out_pipe;
46
47 usb_pipe_t *dpipe = bulk_out_pipe;
48 usb_direction_t ddir = USB_DIRECTION_OUT;
49 size_t dbuf_size = cmd->data_out_size;
50
51 if (cmd->data_in) {
52 ddir = USB_DIRECTION_IN;
53 dbuf_size = cmd->data_in_size;
54 dpipe = bulk_in_pipe;
55 }
56
57 /* Prepare CBW - command block wrapper */
58 usb_massstor_cbw_t cbw;
59 usb_massstor_cbw_prepare(&cbw, tag, dbuf_size, ddir, mfun->lun,
60 cmd->cdb_size, cmd->cdb);
61
62 /* Send the CBW. */
63 MASTLOG("Sending CBW.\n");
64 rc = usb_pipe_write(bulk_out_pipe, &cbw, sizeof(cbw));
65 MASTLOG("CBW '%s' sent: %s.\n",
66 usb_debug_str_buffer((uint8_t *) &cbw, sizeof(cbw), 0),
67 str_error(rc));
68 if (rc != EOK) {
69 usb_log_error("Bulk out write failed: %s", str_error(rc));
70 return EIO;
71 }
72
73 MASTLOG("Transferring data.\n");
74 if (cmd->data_in) {
75 size_t act_size;
76 /* Recieve data from the device. */
77 rc = usb_pipe_read(dpipe, cmd->data_in, cmd->data_in_size,
78 &act_size);
79 MASTLOG("Received %zu bytes (%s): %s.\n", act_size,
80 usb_debug_str_buffer(cmd->data_in, act_size, 0),
81 str_error(rc));
82 }
83 if (cmd->data_out) {
84 /* Send data to the device. */
85 rc = usb_pipe_write(dpipe, cmd->data_out, cmd->data_out_size);
86 MASTLOG("Sent %zu bytes (%s): %s.\n", cmd->data_out_size,
87 usb_debug_str_buffer(cmd->data_out, cmd->data_out_size, 0),
88 str_error(rc));
89 }
90
91 if (rc == ESTALL) {
92 /* Clear stall condition and continue below to read CSW. */
93 usb_pipe_clear_halt(
94 usb_device_get_default_pipe(mfun->mdev->usb_dev), dpipe);
95 } else if (rc != EOK) {
96 usb_log_error("Failed to transfer data: %s", str_error(rc));
97 return EIO;
98 }
99
100 /* Read CSW. */
101 usb_massstor_csw_t csw;
102 size_t csw_size;
103 MASTLOG("Reading CSW.\n");
104 rc = usb_pipe_read(bulk_in_pipe, &csw, sizeof(csw), &csw_size);
105 MASTLOG("CSW '%s' received (%zu bytes): %s.\n",
106 usb_debug_str_buffer((uint8_t *) &csw, csw_size, 0), csw_size,
107 str_error(rc));
108 if (rc != EOK) {
109 usb_log_error("Failed to read CSW: %s", str_error(rc));
110 return EIO;
111 }
112
113 if (csw_size != sizeof(csw)) {
114 usb_log_error("Received CSW of incorrect size.");
115 return EIO;
116 }
117
118 if (csw.dCSWTag != tag) {
119 usb_log_error("Received CSW with incorrect tag. (expected: %"
120 PRIX32 " received: %" PRIx32, tag, csw.dCSWTag);
121 return EIO;
122 }
123
124 /*
125 * Determine the actual return value from the CSW.
126 */
127 switch (csw.dCSWStatus) {
128 case cbs_passed:
129 cmd->status = CMDS_GOOD;
130 break;
131 case cbs_failed:
132 cmd->status = CMDS_FAILED;
133 usb_log_error("CBS Failed.");
134 break;
135 case cbs_phase_error:
136 usb_log_error("CBS phase error.");
137 rc = EIO;
138 break;
139 default:
140 usb_log_error("CBS other error.");
141 rc = EIO;
142 break;
143 }
144
145 const size_t residue = uint32_usb2host(csw.dCSWDataResidue);
146 if (residue > dbuf_size) {
147 usb_log_error("Residue > buffer size (%zu > %zu).",
148 residue, dbuf_size);
149 return EIO;
150 }
151
152 /*
153 * When the device has less data to send than requested (or cannot
154 * receive moredata), it can either stall the pipe or send garbage
155 * (ignore data) and indicate that via the residue field in CSW.
156 * That means dbuf_size - residue is the authoritative size of data
157 * received (sent).
158 */
159
160 if (cmd->data_in)
161 cmd->rcvd_size = dbuf_size - residue;
162
163 return rc;
164}
165
166/** Perform bulk-only mass storage reset.
167 *
168 * @param mfun Mass storage function
169 * @return Error code
170 */
171errno_t usb_massstor_reset(usbmast_dev_t *mdev)
172{
173 return usb_control_request_set(
174 usb_device_get_default_pipe(mdev->usb_dev),
175 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE,
176 0xFF, 0, usb_device_get_iface_number(mdev->usb_dev), NULL, 0);
177}
178
179/** Perform complete reset recovery of bulk-only mass storage.
180 *
181 * Notice that no error is reported because if this fails, the error
182 * would reappear on next transaction somehow.
183 *
184 * @param mfun Mass storage function
185 */
186void usb_massstor_reset_recovery(usbmast_dev_t *mdev)
187{
188 /*
189 * We would ignore errors here because if this fails
190 * we are doomed anyway and any following transaction would fail.
191 */
192 usb_massstor_reset(mdev);
193 usb_pipe_clear_halt(usb_device_get_default_pipe(mdev->usb_dev),
194 mdev->bulk_in_pipe);
195 usb_pipe_clear_halt(usb_device_get_default_pipe(mdev->usb_dev),
196 mdev->bulk_out_pipe);
197}
198
199/** Get max LUN of a mass storage device.
200 *
201 * @see usb_masstor_get_lun_count
202 *
203 * @warning Error from this command does not necessarily indicate malfunction
204 * of the device. Device does not need to support this request.
205 * You shall rather use usb_masstor_get_lun_count.
206 *
207 * @param mfun Mass storage function
208 * @return Maximum LUN (index, not count), or -1
209 */
210int usb_massstor_get_max_lun(usbmast_dev_t *mdev)
211{
212 uint8_t max_lun;
213 size_t data_recv_len;
214 errno_t rc = usb_control_request_get(
215 usb_device_get_default_pipe(mdev->usb_dev),
216 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE,
217 0xFE, 0, usb_device_get_iface_number(mdev->usb_dev), &max_lun, 1,
218 &data_recv_len);
219 if (rc != EOK) {
220 return -1;
221 }
222 if (data_recv_len != 1) {
223 return -1;
224 }
225 return max_lun;
226}
227
228/** Get number of LUNs supported by mass storage device.
229 *
230 * @warning This function hides any error during the request
231 * (typically that shall not be a problem).
232 *
233 * @param mfun Mass storage function
234 * @return Number of LUNs
235 */
236size_t usb_masstor_get_lun_count(usbmast_dev_t *mdev)
237{
238 int max_lun = usb_massstor_get_max_lun(mdev);
239 if (max_lun < 0) {
240 max_lun = 1;
241 } else {
242 max_lun++;
243 }
244
245 return (size_t) max_lun;
246}
247
248/**
249 * @}
250 */
Note: See TracBrowser for help on using the repository browser.