blob: 83c7397b077eff75f065a46cda847da6feba3f48 [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
7#include "util.h" // dprintf
8#include "config.h" // CONFIG_USB_MSC
9#include "usb-msc.h" // usb_msc_init
10#include "usb.h" // struct usb_s
11#include "biosvar.h" // GET_GLOBAL
12#include "blockcmd.h" // cdb_read
13#include "disk.h" // DTYPE_USB
Kevin O'Connor0fedfab2012-03-06 07:18:07 -050014#include "boot.h" // bootprio_find_usb
Kevin O'Connor7149fc82010-02-17 23:24:42 -050015
Kevin O'Connor7149fc82010-02-17 23:24:42 -050016struct usbdrive_s {
17 struct drive_s drive;
18 struct usb_pipe *bulkin, *bulkout;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -040019 int lun;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050020};
21
22
23/****************************************************************
24 * Bulk-only drive command processing
25 ****************************************************************/
26
27#define USB_CDB_SIZE 12
28
29#define CBW_SIGNATURE 0x43425355 // USBC
30
31struct cbw_s {
32 u32 dCBWSignature;
33 u32 dCBWTag;
34 u32 dCBWDataTransferLength;
35 u8 bmCBWFlags;
36 u8 bCBWLUN;
37 u8 bCBWCBLength;
38 u8 CBWCB[16];
39} PACKED;
40
41#define CSW_SIGNATURE 0x53425355 // USBS
42
43struct csw_s {
44 u32 dCSWSignature;
45 u32 dCSWTag;
46 u32 dCSWDataResidue;
47 u8 bCSWStatus;
48} PACKED;
49
Paolo Bonzini092fc352011-11-16 13:02:46 +010050static int
51usb_msc_send(struct usbdrive_s *udrive_g, int dir, void *buf, u32 bytes)
52{
53 struct usb_pipe *pipe;
54 if (dir == USB_DIR_OUT)
55 pipe = GET_GLOBAL(udrive_g->bulkout);
56 else
57 pipe = GET_GLOBAL(udrive_g->bulkin);
58 return usb_send_bulk(pipe, dir, buf, bytes);
59}
60
Kevin O'Connor7149fc82010-02-17 23:24:42 -050061// Low-level usb command transmit function.
62int
63usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
64{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -050065 if (!CONFIG_USB_MSC)
66 return 0;
67
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050068 dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n"
Kevin O'Connor7149fc82010-02-17 23:24:42 -050069 , op->drive_g, 0, op->count, blocksize, op->buf_fl);
70 struct usbdrive_s *udrive_g = container_of(
71 op->drive_g, struct usbdrive_s, drive);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050072
73 // Setup command block wrapper.
74 u32 bytes = blocksize * op->count;
75 struct cbw_s cbw;
76 memset(&cbw, 0, sizeof(cbw));
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010077 memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050078 cbw.dCBWSignature = CBW_SIGNATURE;
79 cbw.dCBWTag = 999; // XXX
80 cbw.dCBWDataTransferLength = bytes;
Kevin O'Connor1fd9a892012-03-14 21:27:45 -040081 cbw.bmCBWFlags = cdb_is_read(cdbcmd, blocksize) ? USB_DIR_IN : USB_DIR_OUT;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -040082 cbw.bCBWLUN = GET_GLOBAL(udrive_g->lun);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050083 cbw.bCBWCBLength = USB_CDB_SIZE;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050084
85 // Transfer cbw to device.
Paolo Bonzini092fc352011-11-16 13:02:46 +010086 int ret = usb_msc_send(udrive_g, USB_DIR_OUT
Kevin O'Connor1fd9a892012-03-14 21:27:45 -040087 , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw));
Kevin O'Connor7149fc82010-02-17 23:24:42 -050088 if (ret)
89 goto fail;
90
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010091 // Transfer data to/from device.
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010092 if (bytes) {
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010093 ret = usb_msc_send(udrive_g, cbw.bmCBWFlags, op->buf_fl, bytes);
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010094 if (ret)
95 goto fail;
96 }
Kevin O'Connor7149fc82010-02-17 23:24:42 -050097
98 // Transfer csw info.
99 struct csw_s csw;
Paolo Bonzini092fc352011-11-16 13:02:46 +0100100 ret = usb_msc_send(udrive_g, USB_DIR_IN
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500101 , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
102 if (ret)
103 goto fail;
104
105 if (!csw.bCSWStatus)
106 return DISK_RET_SUCCESS;
107 if (csw.bCSWStatus == 2)
108 goto fail;
109
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +0100110 if (blocksize)
111 op->count -= csw.dCSWDataResidue / blocksize;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500112 return DISK_RET_EBADTRACK;
113
114fail:
115 // XXX - reset connection
116 dprintf(1, "USB transmission failed\n");
117 op->count = 0;
118 return DISK_RET_EBADTRACK;
119}
120
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400121static int
122usb_msc_maxlun(struct usb_pipe *pipe)
123{
124 struct usb_ctrlrequest req;
125 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
126 req.bRequest = 0xfe;
127 req.wValue = 0;
128 req.wIndex = 0;
129 req.wLength = 1;
130 unsigned char maxlun;
131 int ret = send_default_control(pipe, &req, &maxlun);
132 if (ret)
133 return 0;
134 return maxlun;
135}
136
137static int
138usb_msc_init_lun(struct usb_pipe *inpipe, struct usb_pipe *outpipe,
139 struct usbdevice_s *usbdev, int lun)
140{
141 // Allocate drive structure.
142 struct usbdrive_s *udrive_g = malloc_fseg(sizeof(*udrive_g));
143 if (!udrive_g) {
144 warn_noalloc();
145 return -1;
146 }
147 memset(udrive_g, 0, sizeof(*udrive_g));
148 udrive_g->drive.type = DTYPE_USB;
149 udrive_g->bulkin = inpipe;
150 udrive_g->bulkout = outpipe;
151 udrive_g->lun = lun;
152
153 int prio = bootprio_find_usb(usbdev, lun);
154 int ret = scsi_init_drive(&udrive_g->drive, "USB MSC", prio);
155 if (ret) {
156 dprintf(1, "Unable to configure USB MSC drive.\n");
157 free(udrive_g);
158 return -1;
159 }
160 return 0;
161}
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500162
163/****************************************************************
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500164 * Setup
165 ****************************************************************/
166
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500167// Configure a usb msc device.
168int
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500169usb_msc_init(struct usbdevice_s *usbdev)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500170{
171 if (!CONFIG_USB_MSC)
172 return -1;
173
174 // Verify right kind of device
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500175 struct usb_interface_descriptor *iface = usbdev->iface;
Kevin O'Connor9c000e62010-09-02 21:18:20 -0400176 if ((iface->bInterfaceSubClass != US_SC_SCSI &&
Kevin O'Connor7a08ae72012-03-14 21:11:39 -0400177 iface->bInterfaceSubClass != US_SC_ATAPI_8070 &&
178 iface->bInterfaceSubClass != US_SC_ATAPI_8020)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500179 || iface->bInterfaceProtocol != US_PR_BULK) {
180 dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n"
181 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
182 return -1;
183 }
184
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400185 // Find bulk in and bulk out endpoints.
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400186 struct usb_pipe *inpipe = NULL, *outpipe = NULL;
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400187 struct usb_endpoint_descriptor *indesc = findEndPointDesc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500188 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_IN);
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400189 struct usb_endpoint_descriptor *outdesc = findEndPointDesc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500190 usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400191 if (!indesc || !outdesc)
192 goto fail;
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400193 inpipe = usb_alloc_pipe(usbdev, indesc);
194 outpipe = usb_alloc_pipe(usbdev, outdesc);
195 if (!inpipe || !outpipe)
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400196 goto fail;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500197
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400198 int maxlun = usb_msc_maxlun(usbdev->defpipe);
199 int lun, pipesused = 0;
200 for (lun = 0; lun < maxlun + 1; lun++) {
201 int ret = usb_msc_init_lun(inpipe, outpipe, usbdev, lun);
202 if (!ret)
203 pipesused = 1;
204 }
205
206 if (!pipesused)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500207 goto fail;
208
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500209 return 0;
210fail:
211 dprintf(1, "Unable to configure USB MSC device.\n");
Kevin O'Connor7fa31b52012-06-13 08:47:03 -0400212 free_pipe(inpipe);
213 free_pipe(outpipe);
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500214 return -1;
215}