1 /*
2  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include "esp_log.h"
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <assert.h>
14 #include "esp_check.h"
15 #include "esp_log.h"
16 #include "msc_common.h"
17 #include "msc_scsi_bot.h"
18 
19 #define TAG "USB_MSC_SCSI"
20 
21 /* --------------------------- SCSI Definitions ----------------------------- */
22 #define CMD_SENSE_VALID_BIT (1 << 7)
23 #define SCSI_FLAG_DPO (1<<4)
24 #define SCSI_FLAG_FUA (1<<3)
25 
26 #define SCSI_CMD_FORMAT_UNIT 0x04
27 #define SCSI_CMD_INQUIRY 0x12
28 #define SCSI_CMD_MODE_SELECT 0x55
29 #define SCSI_CMD_MODE_SENSE 0x5A
30 #define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E
31 #define SCSI_CMD_READ10 0x28
32 #define SCSI_CMD_READ12 0xA8
33 #define SCSI_CMD_READ_CAPACITY 0x25
34 #define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23
35 #define SCSI_CMD_REQUEST_SENSE 0x03
36 #define SCSI_CMD_REZERO 0x01
37 #define SCSI_CMD_SEEK10 0x2B
38 #define SCSI_CMD_SEND_DIAGNOSTIC 0x1D
39 #define SCSI_CMD_START_STOP Unit 0x1B
40 #define SCSI_CMD_TEST_UNIT_READY 0x00
41 #define SCSI_CMD_VERIFY 0x2F
42 #define SCSI_CMD_WRITE10 0x2A
43 #define SCSI_CMD_WRITE12 0xAA
44 #define SCSI_CMD_WRITE_AND_VERIFY 0x2E
45 
46 #define IN_DIR   CWB_FLAG_DIRECTION_IN
47 #define OUT_DIR  0
48 
49 #define INQUIRY_VID_SIZE    8
50 #define INQUIRY_PID_SIZE    16
51 #define INQUIRY_REV_SIZE    4
52 
53 #define CBW_CMD_SIZE(cmd) (sizeof(cmd) - sizeof(msc_cbw_t))
54 
55 #define CBW_BASE_INIT(dir, cbw_len, data_len)   \
56     .base = {                                   \
57         .signature = 0x43425355,                \
58         .tag = ++cbw_tag,                       \
59         .flags = dir,                           \
60         .lun = 0,                               \
61         .data_length = data_len,                \
62         .cbw_length = cbw_len,                  \
63     }
64 
65 #define FEATURE_SELECTOR_ENDPOINT   0
66 #define CSW_SIGNATURE   0x53425355
67 #define CBW_SIZE        31
68 
69 #define USB_MASS_REQ_INIT_RESET(ctrl_req_ptr, intf_num) ({                  \
70     (ctrl_req_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT |           \
71                                     USB_BM_REQUEST_TYPE_TYPE_CLASS |        \
72                                     USB_BM_REQUEST_TYPE_RECIP_INTERFACE;    \
73     (ctrl_req_ptr)->bRequest = 0xFF;                                        \
74     (ctrl_req_ptr)->wValue = 0;                                             \
75     (ctrl_req_ptr)->wIndex = (intf_num);                                    \
76     (ctrl_req_ptr)->wLength = 0;                                            \
77 })
78 
79 #define USB_MASS_REQ_INIT_GET_MAX_LUN(ctrl_req_ptr, intf_num) ({            \
80     (ctrl_req_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN |            \
81                                     USB_BM_REQUEST_TYPE_TYPE_CLASS |        \
82                                     USB_BM_REQUEST_TYPE_RECIP_INTERFACE;    \
83     (ctrl_req_ptr)->bRequest = 0xFE;                                        \
84     (ctrl_req_ptr)->wValue = 0;                                             \
85     (ctrl_req_ptr)->wIndex = (intf_num);                                    \
86     (ctrl_req_ptr)->wLength = 1;                                            \
87 })
88 
89 #define USB_SETUP_PACKET_INIT_CLEAR_FEATURE_EP(ctrl_req_ptr, ep_num) ({     \
90     (ctrl_req_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT |           \
91                                     USB_BM_REQUEST_TYPE_TYPE_STANDARD |     \
92                                     USB_BM_REQUEST_TYPE_RECIP_ENDPOINT;     \
93     (ctrl_req_ptr)->bRequest = USB_B_REQUEST_CLEAR_FEATURE;                 \
94     (ctrl_req_ptr)->wValue = FEATURE_SELECTOR_ENDPOINT;                     \
95     (ctrl_req_ptr)->wIndex = (ep_num);                                      \
96     (ctrl_req_ptr)->wLength = 0;                                            \
97 })
98 
99 #define CWB_FLAG_DIRECTION_IN (1<<7) // device -> host
100 
101 /**
102  * @brief Command Block Wrapper structure
103  */
104 typedef struct __attribute__((packed))
105 {
106     uint32_t signature;
107     uint32_t tag;
108     uint32_t data_length;
109     uint8_t flags;
110     uint8_t lun;
111     uint8_t cbw_length;
112 } msc_cbw_t;
113 
114 /**
115  * @brief Command Status Wrapper structure
116  */
117 typedef struct __attribute__((packed))
118 {
119     uint32_t signature;
120     uint32_t tag;
121     uint32_t dataResidue;
122     uint8_t status;
123 } msc_csw_t;
124 
125 typedef struct __attribute__((packed))
126 {
127     msc_cbw_t base;
128     uint8_t opcode;
129     uint8_t flags;
130     uint32_t address;
131     uint8_t reserved1;
132     uint16_t length;
133     uint8_t reserved2[3];
134 } cbw_read10_t;
135 
136 typedef struct __attribute__((packed))
137 {
138     msc_cbw_t base;
139     uint8_t opcode;
140     uint8_t flags;
141     uint32_t address;
142     uint8_t reserved1;
143     uint16_t length;
144     uint8_t reserved2[1];
145 } cbw_write10_t;
146 
147 typedef struct __attribute__((packed))
148 {
149     msc_cbw_t base;
150     uint8_t opcode;
151     uint8_t flags;
152     uint32_t address;
153     uint8_t reserved[6];
154 } cbw_read_capacity_t;
155 
156 typedef struct __attribute__((packed))
157 {
158     uint32_t block_count;
159     uint32_t block_size;
160 } cbw_read_capacity_response_t;
161 
162 typedef struct __attribute__((packed))
163 {
164     msc_cbw_t base;
165     uint8_t opcode;
166     uint8_t flags;
167     uint8_t reserved[10];
168 } cbw_unit_ready_t;
169 
170 typedef struct __attribute__((packed))
171 {
172     msc_cbw_t base;
173     uint8_t opcode;
174     uint8_t flags;
175     uint8_t reserved_0[2];
176     uint8_t allocation_length;
177     uint8_t reserved_1[7];
178 } cbw_sense_t;
179 
180 typedef struct __attribute__((packed))
181 {
182     uint8_t error_code;
183     uint8_t reserved_0;
184     uint8_t sense_key;
185     uint32_t info;
186     uint8_t sense_len;
187     uint32_t reserved_1;
188     uint8_t sense_code;
189     uint8_t sense_code_qualifier;
190     uint32_t reserved_2;
191 } cbw_sense_response_t;
192 
193 typedef struct __attribute__((packed))
194 {
195     msc_cbw_t base;
196     uint8_t opcode;
197     uint8_t flags;
198     uint8_t page_code;
199     uint8_t reserved_0;
200     uint8_t allocation_length;
201     uint8_t reserved_1[7];
202 } cbw_inquiry_t;
203 
204 typedef struct __attribute__((packed))
205 {
206     msc_cbw_t base;
207     uint8_t opcode;
208     uint8_t flags;
209     uint8_t pc_page_code;
210     uint8_t reserved_1[4];
211     uint16_t parameter_list_length;
212     uint8_t reserved_2[3];
213 } mode_sense_t;
214 
215 typedef struct __attribute__((packed))
216 {
217     uint8_t data[8];
218 } mode_sense_response_t;
219 
220 typedef struct __attribute__((packed))
221 {
222     msc_cbw_t base;
223     uint8_t opcode;
224     uint8_t flags;
225     uint8_t reserved_1[2];
226     uint8_t prevent;
227     uint8_t reserved_2[7];
228 } prevent_allow_medium_removal_t;
229 
230 typedef struct __attribute__((packed))
231 {
232     uint8_t data[36];
233 } cbw_inquiry_response_t;
234 
235 // Unique number based on which MSC protocol pairs request and response
236 static uint32_t cbw_tag;
237 
check_csw(msc_csw_t * csw,uint32_t tag)238 static esp_err_t check_csw(msc_csw_t *csw, uint32_t tag)
239 {
240     bool csw_ok = csw->signature == CSW_SIGNATURE && csw->tag == tag &&
241                   csw->dataResidue == 0 && csw->status == 0;
242 
243     if (!csw_ok) {
244         ESP_LOGD(TAG, "CSW failed: status %d", csw->status);
245     }
246 
247     return csw_ok ? ESP_OK : ESP_FAIL;
248 }
249 
clear_feature(msc_device_t * device,uint8_t endpoint)250 static esp_err_t clear_feature(msc_device_t *device, uint8_t endpoint)
251 {
252     usb_device_handle_t dev = device->handle;
253     usb_transfer_t *xfer = device->xfer;
254 
255     MSC_RETURN_ON_ERROR( usb_host_endpoint_clear(dev, endpoint) );
256     USB_SETUP_PACKET_INIT_CLEAR_FEATURE_EP((usb_setup_packet_t *)xfer->data_buffer, endpoint);
257     MSC_RETURN_ON_ERROR( msc_control_transfer(device, xfer, USB_SETUP_PACKET_SIZE) );
258 
259     return ESP_OK;
260 }
261 
msc_mass_reset(msc_device_t * device)262 esp_err_t msc_mass_reset(msc_device_t *device)
263 {
264     usb_transfer_t *xfer = device->xfer;
265 
266     USB_MASS_REQ_INIT_RESET((usb_setup_packet_t *)xfer->data_buffer, 0);
267     MSC_RETURN_ON_ERROR( msc_control_transfer(device, xfer, USB_SETUP_PACKET_SIZE) );
268 
269     return ESP_OK;
270 }
271 
msc_get_max_lun(msc_device_t * device,uint8_t * lun)272 esp_err_t msc_get_max_lun(msc_device_t *device, uint8_t *lun)
273 {
274     usb_transfer_t *xfer = device->xfer;
275 
276     USB_MASS_REQ_INIT_GET_MAX_LUN((usb_setup_packet_t *)xfer->data_buffer, 0);
277     MSC_RETURN_ON_ERROR( msc_control_transfer(device, xfer, USB_SETUP_PACKET_SIZE + 1) );
278 
279     *lun = xfer->data_buffer[USB_SETUP_PACKET_SIZE];
280 
281     return ESP_OK;
282 }
283 
bot_execute_command(msc_device_t * device,msc_cbw_t * cbw,void * data,size_t size)284 static esp_err_t bot_execute_command(msc_device_t *device, msc_cbw_t *cbw, void *data, size_t size)
285 {
286     msc_csw_t csw;
287     msc_endpoint_t ep = (cbw->flags & CWB_FLAG_DIRECTION_IN) ? MSC_EP_IN : MSC_EP_OUT;
288 
289     MSC_RETURN_ON_ERROR( msc_bulk_transfer(device, (uint8_t *)cbw, CBW_SIZE, MSC_EP_OUT) );
290 
291     if (data) {
292         MSC_RETURN_ON_ERROR( msc_bulk_transfer(device, (uint8_t *)data, size, ep) );
293     }
294 
295     esp_err_t  err = msc_bulk_transfer(device, (uint8_t *)&csw, sizeof(msc_csw_t), MSC_EP_IN);
296 
297     if (err ==  ESP_FAIL && device->transfer_status == USB_TRANSFER_STATUS_STALL) {
298         ESP_RETURN_ON_ERROR( clear_feature(device, MSC_EP_IN), TAG, "Clear feature failed" );
299         // Try to read csw again after clearing feature
300         err = msc_bulk_transfer(device, (uint8_t *)&csw, sizeof(msc_csw_t), MSC_EP_IN);
301         if (err) {
302             ESP_RETURN_ON_ERROR( clear_feature(device, MSC_EP_IN), TAG, "Clear feature failed" );
303             ESP_RETURN_ON_ERROR( msc_mass_reset(device), TAG, "Mass reset failed" );
304             return ESP_FAIL;
305         }
306     }
307 
308     MSC_RETURN_ON_ERROR(err);
309 
310     return check_csw(&csw, cbw->tag);
311 }
312 
313 
scsi_cmd_read10(msc_device_t * device,uint8_t * data,uint32_t sector_address,uint32_t num_sectors,uint32_t sector_size)314 esp_err_t scsi_cmd_read10(msc_device_t *device,
315                           uint8_t *data,
316                           uint32_t sector_address,
317                           uint32_t num_sectors,
318                           uint32_t sector_size)
319 {
320     cbw_read10_t cbw = {
321         CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_read10_t), num_sectors * sector_size),
322         .opcode = SCSI_CMD_READ10,
323         .flags = 0, // lun
324         .address = __builtin_bswap32(sector_address),
325         .length = __builtin_bswap16(num_sectors),
326     };
327 
328     return bot_execute_command(device, &cbw.base, data, num_sectors * sector_size);
329 }
330 
scsi_cmd_write10(msc_device_t * device,const uint8_t * data,uint32_t sector_address,uint32_t num_sectors,uint32_t sector_size)331 esp_err_t scsi_cmd_write10(msc_device_t *device,
332                            const uint8_t *data,
333                            uint32_t sector_address,
334                            uint32_t num_sectors,
335                            uint32_t sector_size)
336 {
337     cbw_write10_t cbw = {
338         CBW_BASE_INIT(OUT_DIR, CBW_CMD_SIZE(cbw_write10_t), num_sectors * sector_size),
339         .opcode = SCSI_CMD_WRITE10,
340         .address = __builtin_bswap32(sector_address),
341         .length = __builtin_bswap16(num_sectors),
342     };
343 
344     return bot_execute_command(device, &cbw.base, (void *)data, num_sectors * sector_size);
345 }
346 
scsi_cmd_read_capacity(msc_device_t * device,uint32_t * block_size,uint32_t * block_count)347 esp_err_t scsi_cmd_read_capacity(msc_device_t *device, uint32_t *block_size, uint32_t *block_count)
348 {
349     cbw_read_capacity_response_t response;
350 
351     cbw_read_capacity_t cbw = {
352         CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_read_capacity_t), sizeof(response)),
353         .opcode = SCSI_CMD_READ_CAPACITY,
354     };
355 
356     MSC_RETURN_ON_ERROR( bot_execute_command(device, &cbw.base, &response, sizeof(response)) );
357 
358     *block_count = __builtin_bswap32(response.block_count);
359     *block_size = __builtin_bswap32(response.block_size);
360 
361     return ESP_OK;
362 }
363 
scsi_cmd_unit_ready(msc_device_t * device)364 esp_err_t scsi_cmd_unit_ready(msc_device_t *device)
365 {
366     cbw_unit_ready_t cbw = {
367         CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_unit_ready_t), 0),
368         .opcode = SCSI_CMD_TEST_UNIT_READY,
369     };
370 
371     return bot_execute_command(device, &cbw.base, NULL, 0);
372 }
373 
scsi_cmd_sense(msc_device_t * device,scsi_sense_data_t * sense)374 esp_err_t scsi_cmd_sense(msc_device_t *device, scsi_sense_data_t *sense)
375 {
376     cbw_sense_response_t response;
377 
378     cbw_sense_t cbw = {
379         CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_sense_t), sizeof(response)),
380         .opcode = SCSI_CMD_REQUEST_SENSE,
381         .allocation_length = sizeof(response),
382     };
383 
384     MSC_RETURN_ON_ERROR( bot_execute_command(device, &cbw.base, &response, sizeof(response)) );
385 
386     if (sense->key) {
387         ESP_LOGD(TAG, "sense_key: 0x%02X, code: 0x%02X, qualifier: 0x%02X",
388                  response.sense_key, response.sense_code, response.sense_code_qualifier);
389     }
390 
391     sense->key = response.sense_key;
392     sense->code = response.sense_code;
393     sense->code_q = response.sense_code_qualifier;
394 
395     return ESP_OK;
396 }
397 
scsi_cmd_inquiry(msc_device_t * device)398 esp_err_t scsi_cmd_inquiry(msc_device_t *device)
399 {
400     cbw_inquiry_response_t response = { 0 };
401 
402     cbw_inquiry_t cbw = {
403         CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_inquiry_t), sizeof(response)),
404         .opcode = SCSI_CMD_INQUIRY,
405         .allocation_length = sizeof(response),
406     };
407 
408     return bot_execute_command(device, &cbw.base, &response, sizeof(response) );
409 }
410 
scsi_cmd_mode_sense(msc_device_t * device)411 esp_err_t scsi_cmd_mode_sense(msc_device_t *device)
412 {
413     mode_sense_response_t response = { 0 };
414 
415     mode_sense_t cbw = {
416         CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(mode_sense_t), sizeof(response)),
417         .opcode = SCSI_CMD_MODE_SENSE,
418         .pc_page_code = 0x3F,
419         .parameter_list_length = sizeof(response),
420     };
421 
422     return bot_execute_command(device, &cbw.base, &response, sizeof(response) );
423 }
424 
scsi_cmd_prevent_removal(msc_device_t * device,bool prevent)425 esp_err_t scsi_cmd_prevent_removal(msc_device_t *device, bool prevent)
426 {
427     prevent_allow_medium_removal_t cbw = {
428         CBW_BASE_INIT(OUT_DIR, CBW_CMD_SIZE(prevent_allow_medium_removal_t), 0),
429         .opcode = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL,
430         .prevent = 1,
431     };
432 
433     return bot_execute_command(device, &cbw.base, NULL, 0);
434 }
435