blob: a7af9a7ba2933134eb0d9cd2d9febd3dffacc246 [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
66usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
67{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -050068 if (!CONFIG_USB_MSC)
69 return 0;
70
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050071 dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n"
Kevin O'Connor1902c942013-10-26 11:48:06 -040072 , op->drive_gf, 0, op->count, blocksize, op->buf_fl);
73 struct usbdrive_s *udrive_gf = container_of(
74 op->drive_gf, struct usbdrive_s, drive);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050075
76 // Setup command block wrapper.
77 u32 bytes = blocksize * op->count;
78 struct cbw_s cbw;
79 memset(&cbw, 0, sizeof(cbw));
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010080 memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050081 cbw.dCBWSignature = CBW_SIGNATURE;
82 cbw.dCBWTag = 999; // XXX
83 cbw.dCBWDataTransferLength = bytes;
Kevin O'Connor1fd9a892012-03-14 21:27:45 -040084 cbw.bmCBWFlags = cdb_is_read(cdbcmd, blocksize) ? USB_DIR_IN : USB_DIR_OUT;
Kevin O'Connor1902c942013-10-26 11:48:06 -040085 cbw.bCBWLUN = GET_GLOBALFLAT(udrive_gf->lun);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050086 cbw.bCBWCBLength = USB_CDB_SIZE;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050087
88 // Transfer cbw to device.
Kevin O'Connor1902c942013-10-26 11:48:06 -040089 int ret = usb_msc_send(udrive_gf, USB_DIR_OUT
Kevin O'Connor1fd9a892012-03-14 21:27:45 -040090 , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw));
Kevin O'Connor7149fc82010-02-17 23:24:42 -050091 if (ret)
92 goto fail;
93
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010094 // Transfer data to/from device.
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010095 if (bytes) {
Kevin O'Connor1902c942013-10-26 11:48:06 -040096 ret = usb_msc_send(udrive_gf, cbw.bmCBWFlags, op->buf_fl, bytes);
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010097 if (ret)
98 goto fail;
99 }
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500100
101 // Transfer csw info.
102 struct csw_s csw;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400103 ret = usb_msc_send(udrive_gf, USB_DIR_IN
104 , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500105 if (ret)
106 goto fail;
107
108 if (!csw.bCSWStatus)
109 return DISK_RET_SUCCESS;
110 if (csw.bCSWStatus == 2)
111 goto fail;
112
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +0100113 if (blocksize)
114 op->count -= csw.dCSWDataResidue / blocksize;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500115 return DISK_RET_EBADTRACK;
116
117fail:
118 // XXX - reset connection
119 dprintf(1, "USB transmission failed\n");
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500120 return DISK_RET_EBADTRACK;
121}
122
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400123static int
124usb_msc_maxlun(struct usb_pipe *pipe)
125{
126 struct usb_ctrlrequest req;
127 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
128 req.bRequest = 0xfe;
129 req.wValue = 0;
130 req.wIndex = 0;
131 req.wLength = 1;
132 unsigned char maxlun;
133 int ret = send_default_control(pipe, &req, &maxlun);
134 if (ret)
135 return 0;
136 return maxlun;
137}
138
139static int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500140usb_msc_lun_setup(struct usb_pipe *inpipe, struct usb_pipe *outpipe,
141 struct usbdevice_s *usbdev, int lun)
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400142{
143 // Allocate drive structure.
Kevin O'Connor1902c942013-10-26 11:48:06 -0400144 struct usbdrive_s *drive = malloc_fseg(sizeof(*drive));
145 if (!drive) {
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400146 warn_noalloc();
147 return -1;
148 }
Kevin O'Connor1902c942013-10-26 11:48:06 -0400149 memset(drive, 0, sizeof(*drive));
Kevin O'Connorde30dad2013-12-30 22:09:04 -0500150 if (usb_32bit_pipe(inpipe))
151 drive->drive.type = DTYPE_USB_32;
152 else
153 drive->drive.type = DTYPE_USB;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400154 drive->bulkin = inpipe;
155 drive->bulkout = outpipe;
156 drive->lun = lun;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400157
158 int prio = bootprio_find_usb(usbdev, lun);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400159 int ret = scsi_drive_setup(&drive->drive, "USB MSC", prio);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400160 if (ret) {
161 dprintf(1, "Unable to configure USB MSC drive.\n");
Kevin O'Connor1902c942013-10-26 11:48:06 -0400162 free(drive);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400163 return -1;
164 }
165 return 0;
166}
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500167
168/****************************************************************
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500169 * Setup
170 ****************************************************************/
171
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500172// Configure a usb msc device.
173int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500174usb_msc_setup(struct usbdevice_s *usbdev)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500175{
176 if (!CONFIG_USB_MSC)
177 return -1;
178
179 // Verify right kind of device
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500180 struct usb_interface_descriptor *iface = usbdev->iface;
Kevin O'Connor9c000e62010-09-02 21:18:20 -0400181 if ((iface->bInterfaceSubClass != US_SC_SCSI &&
Kevin O'Connor7a08ae72012-03-14 21:11:39 -0400182 iface->bInterfaceSubClass != US_SC_ATAPI_8070 &&
183 iface->bInterfaceSubClass != US_SC_ATAPI_8020)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500184 || iface->bInterfaceProtocol != US_PR_BULK) {
185 dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n"
186 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
187 return -1;
188 }
189
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400190 // Find bulk in and bulk out endpoints.
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400191 struct usb_pipe *inpipe = NULL, *outpipe = NULL;
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400192 struct usb_endpoint_descriptor *indesc = findEndPointDesc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500193 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_IN);
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400194 struct usb_endpoint_descriptor *outdesc = findEndPointDesc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500195 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400196 if (!indesc || !outdesc)
197 goto fail;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400198 inpipe = usb_alloc_pipe(usbdev, indesc);
199 outpipe = usb_alloc_pipe(usbdev, outdesc);
200 if (!inpipe || !outpipe)
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400201 goto fail;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500202
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400203 int maxlun = usb_msc_maxlun(usbdev->defpipe);
204 int lun, pipesused = 0;
205 for (lun = 0; lun < maxlun + 1; lun++) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500206 int ret = usb_msc_lun_setup(inpipe, outpipe, usbdev, lun);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400207 if (!ret)
208 pipesused = 1;
209 }
210
211 if (!pipesused)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500212 goto fail;
213
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500214 return 0;
215fail:
216 dprintf(1, "Unable to configure USB MSC device.\n");
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400217 free_pipe(inpipe);
218 free_pipe(outpipe);
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500219 return -1;
220}