blob: 8ee448f03484a877b4da1b125980222f82844d86 [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'Connor7149fc82010-02-17 23:24:42 -05007#include "biosvar.h" // GET_GLOBAL
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
54usb_msc_send(struct usbdrive_s *udrive_g, int dir, void *buf, u32 bytes)
55{
56 struct usb_pipe *pipe;
57 if (dir == USB_DIR_OUT)
58 pipe = GET_GLOBAL(udrive_g->bulkout);
59 else
60 pipe = GET_GLOBAL(udrive_g->bulkin);
61 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'Connor7149fc82010-02-17 23:24:42 -050072 , op->drive_g, 0, op->count, blocksize, op->buf_fl);
73 struct usbdrive_s *udrive_g = container_of(
74 op->drive_g, 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'Connor7fa31b52012-06-13 08:47:03 -040085 cbw.bCBWLUN = GET_GLOBAL(udrive_g->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.
Paolo Bonzini092fc352011-11-16 13:02:46 +010089 int ret = usb_msc_send(udrive_g, 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) {
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010096 ret = usb_msc_send(udrive_g, 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;
Paolo Bonzini092fc352011-11-16 13:02:46 +0100103 ret = usb_msc_send(udrive_g, USB_DIR_IN
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500104 , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
105 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");
120 op->count = 0;
121 return DISK_RET_EBADTRACK;
122}
123
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400124static int
125usb_msc_maxlun(struct usb_pipe *pipe)
126{
127 struct usb_ctrlrequest req;
128 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
129 req.bRequest = 0xfe;
130 req.wValue = 0;
131 req.wIndex = 0;
132 req.wLength = 1;
133 unsigned char maxlun;
134 int ret = send_default_control(pipe, &req, &maxlun);
135 if (ret)
136 return 0;
137 return maxlun;
138}
139
140static int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500141usb_msc_lun_setup(struct usb_pipe *inpipe, struct usb_pipe *outpipe,
142 struct usbdevice_s *usbdev, int lun)
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400143{
144 // Allocate drive structure.
145 struct usbdrive_s *udrive_g = malloc_fseg(sizeof(*udrive_g));
146 if (!udrive_g) {
147 warn_noalloc();
148 return -1;
149 }
150 memset(udrive_g, 0, sizeof(*udrive_g));
151 udrive_g->drive.type = DTYPE_USB;
152 udrive_g->bulkin = inpipe;
153 udrive_g->bulkout = outpipe;
154 udrive_g->lun = lun;
155
156 int prio = bootprio_find_usb(usbdev, lun);
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500157 int ret = scsi_drive_setup(&udrive_g->drive, "USB MSC", prio);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400158 if (ret) {
159 dprintf(1, "Unable to configure USB MSC drive.\n");
160 free(udrive_g);
161 return -1;
162 }
163 return 0;
164}
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500165
166/****************************************************************
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500167 * Setup
168 ****************************************************************/
169
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500170// Configure a usb msc device.
171int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500172usb_msc_setup(struct usbdevice_s *usbdev)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500173{
174 if (!CONFIG_USB_MSC)
175 return -1;
176
177 // Verify right kind of device
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500178 struct usb_interface_descriptor *iface = usbdev->iface;
Kevin O'Connor9c000e62010-09-02 21:18:20 -0400179 if ((iface->bInterfaceSubClass != US_SC_SCSI &&
Kevin O'Connor7a08ae72012-03-14 21:11:39 -0400180 iface->bInterfaceSubClass != US_SC_ATAPI_8070 &&
181 iface->bInterfaceSubClass != US_SC_ATAPI_8020)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500182 || iface->bInterfaceProtocol != US_PR_BULK) {
183 dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n"
184 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
185 return -1;
186 }
187
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400188 // Find bulk in and bulk out endpoints.
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400189 struct usb_pipe *inpipe = NULL, *outpipe = NULL;
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400190 struct usb_endpoint_descriptor *indesc = findEndPointDesc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500191 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_IN);
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400192 struct usb_endpoint_descriptor *outdesc = findEndPointDesc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500193 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400194 if (!indesc || !outdesc)
195 goto fail;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400196 inpipe = usb_alloc_pipe(usbdev, indesc);
197 outpipe = usb_alloc_pipe(usbdev, outdesc);
198 if (!inpipe || !outpipe)
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400199 goto fail;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500200
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400201 int maxlun = usb_msc_maxlun(usbdev->defpipe);
202 int lun, pipesused = 0;
203 for (lun = 0; lun < maxlun + 1; lun++) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500204 int ret = usb_msc_lun_setup(inpipe, outpipe, usbdev, lun);
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400205 if (!ret)
206 pipesused = 1;
207 }
208
209 if (!pipesused)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500210 goto fail;
211
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500212 return 0;
213fail:
214 dprintf(1, "Unable to configure USB MSC device.\n");
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400215 free_pipe(inpipe);
216 free_pipe(outpipe);
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500217 return -1;
218}