blob: 2b188289b8f0b4a67f6923920696a7e02488ecb9 [file] [log] [blame]
Kevin O'Connor7149fc82010-02-17 23:24:42 -05001// Code for handling USB Mass Storage Controller devices.
2//
3// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
Kevin O'Connor1902c942013-10-26 11:48:06 -04007#include "biosvar.h" // GET_GLOBALFLAT
Kevin O'Connor135f3f62013-09-14 23:57:26 -04008#include "block.h" // DTYPE_USB
Kevin O'Connor7149fc82010-02-17 23:24:42 -05009#include "blockcmd.h" // cdb_read
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "config.h" // CONFIG_USB_MSC
Kevin O'Connor9dea5902013-09-14 20:23:54 -040011#include "malloc.h" // free
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "output.h" // dprintf
Kevin O'Connor135f3f62013-09-14 23:57:26 -040013#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040014#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040015#include "usb.h" // struct usb_s
16#include "usb-msc.h" // usb_msc_setup
Kevin O'Connor07cf73b2013-09-15 00:31:17 -040017#include "util.h" // bootprio_find_usb
Kevin O'Connor7149fc82010-02-17 23:24:42 -050018
Kevin O'Connor7149fc82010-02-17 23:24:42 -050019struct usbdrive_s {
20 struct drive_s drive;
21 struct usb_pipe *bulkin, *bulkout;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -040022 int lun;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050023};
24
25
26/****************************************************************
27 * Bulk-only drive command processing
28 ****************************************************************/
29
30#define USB_CDB_SIZE 12
31
32#define CBW_SIGNATURE 0x43425355 // USBC
33
34struct cbw_s {
35 u32 dCBWSignature;
36 u32 dCBWTag;
37 u32 dCBWDataTransferLength;
38 u8 bmCBWFlags;
39 u8 bCBWLUN;
40 u8 bCBWCBLength;
41 u8 CBWCB[16];
42} PACKED;
43
44#define CSW_SIGNATURE 0x53425355 // USBS
45
46struct csw_s {
47 u32 dCSWSignature;
48 u32 dCSWTag;
49 u32 dCSWDataResidue;
50 u8 bCSWStatus;
51} PACKED;
52
Paolo Bonzini092fc352011-11-16 13:02:46 +010053static int
Kevin O'Connor1902c942013-10-26 11:48:06 -040054usb_msc_send(struct usbdrive_s *udrive_gf, int dir, void *buf, u32 bytes)
Paolo Bonzini092fc352011-11-16 13:02:46 +010055{
56 struct usb_pipe *pipe;
57 if (dir == USB_DIR_OUT)
Kevin O'Connor1902c942013-10-26 11:48:06 -040058 pipe = GET_GLOBALFLAT(udrive_gf->bulkout);
Paolo Bonzini092fc352011-11-16 13:02:46 +010059 else
Kevin O'Connor1902c942013-10-26 11:48:06 -040060 pipe = GET_GLOBALFLAT(udrive_gf->bulkin);
Paolo Bonzini092fc352011-11-16 13:02:46 +010061 return usb_send_bulk(pipe, dir, buf, bytes);
62}
63
Kevin O'Connor7149fc82010-02-17 23:24:42 -050064// Low-level usb command transmit function.
65int
Kevin O'Connored120902015-07-07 11:35:25 -040066usb_process_op(struct disk_op_s *op)
Kevin O'Connor7149fc82010-02-17 23:24:42 -050067{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -050068 if (!CONFIG_USB_MSC)
69 return 0;
70
Kevin O'Connored120902015-07-07 11:35:25 -040071 dprintf(16, "usb_cmd_data id=%p write=%d count=%d buf=%p\n"
Kevin O'Connore5a0b612017-07-11 12:24:50 -040072 , op->drive_fl, 0, op->count, op->buf_fl);
Kevin O'Connor1902c942013-10-26 11:48:06 -040073 struct usbdrive_s *udrive_gf = container_of(
Kevin O'Connore5a0b612017-07-11 12:24:50 -040074 op->drive_fl, struct usbdrive_s, drive);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050075
76 // Setup command block wrapper.
Kevin O'Connor7149fc82010-02-17 23:24:42 -050077 struct cbw_s cbw;
78 memset(&cbw, 0, sizeof(cbw));
Kevin O'Connored120902015-07-07 11:35:25 -040079 int blocksize = scsi_fill_cmd(op, cbw.CBWCB, USB_CDB_SIZE);
80 if (blocksize < 0)
81 return default_process_op(op);
82 u32 bytes = blocksize * op->count;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050083 cbw.dCBWSignature = CBW_SIGNATURE;
84 cbw.dCBWTag = 999; // XXX
85 cbw.dCBWDataTransferLength = bytes;
Kevin O'Connor5dcd1ee2015-07-07 14:43:01 -040086 cbw.bmCBWFlags = scsi_is_read(op) ? USB_DIR_IN : USB_DIR_OUT;
Kevin O'Connor1902c942013-10-26 11:48:06 -040087 cbw.bCBWLUN = GET_GLOBALFLAT(udrive_gf->lun);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050088 cbw.bCBWCBLength = USB_CDB_SIZE;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050089
90 // Transfer cbw to device.
Kevin O'Connor1902c942013-10-26 11:48:06 -040091 int ret = usb_msc_send(udrive_gf, USB_DIR_OUT
Kevin O'Connor1fd9a892012-03-14 21:27:45 -040092 , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw));
Kevin O'Connor7149fc82010-02-17 23:24:42 -050093 if (ret)
94 goto fail;
95
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010096 // Transfer data to/from device.
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010097 if (bytes) {
Kevin O'Connor1902c942013-10-26 11:48:06 -040098 ret = usb_msc_send(udrive_gf, cbw.bmCBWFlags, op->buf_fl, bytes);
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010099 if (ret)
100 goto fail;
101 }
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500102
103 // Transfer csw info.
104 struct csw_s csw;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400105 ret = usb_msc_send(udrive_gf, USB_DIR_IN
106 , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500107 if (ret)
108 goto fail;
109
110 if (!csw.bCSWStatus)
111 return DISK_RET_SUCCESS;
112 if (csw.bCSWStatus == 2)
113 goto fail;
114
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +0100115 if (blocksize)
116 op->count -= csw.dCSWDataResidue / blocksize;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500117 return DISK_RET_EBADTRACK;
118
119fail:
120 // XXX - reset connection
121 dprintf(1, "USB transmission failed\n");
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500122 return DISK_RET_EBADTRACK;
123}
124
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400125static int
126usb_msc_maxlun(struct usb_pipe *pipe)
127{
128 struct usb_ctrlrequest req;
129 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
130 req.bRequest = 0xfe;
131 req.wValue = 0;
132 req.wIndex = 0;
133 req.wLength = 1;
134 unsigned char maxlun;
Kevin O'Connor222bad42014-10-16 12:11:12 -0400135 int ret = usb_send_default_control(pipe, &req, &maxlun);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400136 if (ret)
137 return 0;
138 return maxlun;
139}
140
141static int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500142usb_msc_lun_setup(struct usb_pipe *inpipe, struct usb_pipe *outpipe,
143 struct usbdevice_s *usbdev, int lun)
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400144{
145 // Allocate drive structure.
Kevin O'Connor1902c942013-10-26 11:48:06 -0400146 struct usbdrive_s *drive = malloc_fseg(sizeof(*drive));
147 if (!drive) {
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400148 warn_noalloc();
149 return -1;
150 }
Kevin O'Connor1902c942013-10-26 11:48:06 -0400151 memset(drive, 0, sizeof(*drive));
Kevin O'Connorde30dad2013-12-30 22:09:04 -0500152 if (usb_32bit_pipe(inpipe))
153 drive->drive.type = DTYPE_USB_32;
154 else
155 drive->drive.type = DTYPE_USB;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400156 drive->bulkin = inpipe;
157 drive->bulkout = outpipe;
158 drive->lun = lun;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400159
160 int prio = bootprio_find_usb(usbdev, lun);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400161 int ret = scsi_drive_setup(&drive->drive, "USB MSC", prio);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400162 if (ret) {
163 dprintf(1, "Unable to configure USB MSC drive.\n");
Kevin O'Connor1902c942013-10-26 11:48:06 -0400164 free(drive);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400165 return -1;
166 }
167 return 0;
168}
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500169
170/****************************************************************
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500171 * Setup
172 ****************************************************************/
173
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500174// Configure a usb msc device.
175int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500176usb_msc_setup(struct usbdevice_s *usbdev)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500177{
178 if (!CONFIG_USB_MSC)
179 return -1;
180
181 // Verify right kind of device
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500182 struct usb_interface_descriptor *iface = usbdev->iface;
Kevin O'Connor9c000e62010-09-02 21:18:20 -0400183 if ((iface->bInterfaceSubClass != US_SC_SCSI &&
Kevin O'Connor7a08ae72012-03-14 21:11:39 -0400184 iface->bInterfaceSubClass != US_SC_ATAPI_8070 &&
185 iface->bInterfaceSubClass != US_SC_ATAPI_8020)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500186 || iface->bInterfaceProtocol != US_PR_BULK) {
187 dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n"
188 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
189 return -1;
190 }
191
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400192 // Find bulk in and bulk out endpoints.
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400193 struct usb_pipe *inpipe = NULL, *outpipe = NULL;
Kevin O'Connor674f1402014-10-16 12:08:00 -0400194 struct usb_endpoint_descriptor *indesc = usb_find_desc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500195 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_IN);
Kevin O'Connor674f1402014-10-16 12:08:00 -0400196 struct usb_endpoint_descriptor *outdesc = usb_find_desc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500197 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400198 if (!indesc || !outdesc)
199 goto fail;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400200 inpipe = usb_alloc_pipe(usbdev, indesc);
201 outpipe = usb_alloc_pipe(usbdev, outdesc);
202 if (!inpipe || !outpipe)
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400203 goto fail;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500204
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400205 int maxlun = usb_msc_maxlun(usbdev->defpipe);
206 int lun, pipesused = 0;
207 for (lun = 0; lun < maxlun + 1; lun++) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500208 int ret = usb_msc_lun_setup(inpipe, outpipe, usbdev, lun);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400209 if (!ret)
210 pipesused = 1;
211 }
212
213 if (!pipesused)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500214 goto fail;
215
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500216 return 0;
217fail:
218 dprintf(1, "Unable to configure USB MSC device.\n");
Kevin O'Connorf3329432014-10-16 11:59:45 -0400219 usb_free_pipe(usbdev, inpipe);
220 usb_free_pipe(usbdev, outpipe);
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500221 return -1;
222}