blob: ef9f4408f3b85c7f857deacc0ea272f01e644f51 [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'Connor72eee3e2010-12-27 19:07:49 -050014#include "boot.h" // boot_add_hd
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'Connor7149fc82010-02-17 23:24:42 -050019};
20
21
22/****************************************************************
23 * Bulk-only drive command processing
24 ****************************************************************/
25
26#define USB_CDB_SIZE 12
27
28#define CBW_SIGNATURE 0x43425355 // USBC
29
30struct cbw_s {
31 u32 dCBWSignature;
32 u32 dCBWTag;
33 u32 dCBWDataTransferLength;
34 u8 bmCBWFlags;
35 u8 bCBWLUN;
36 u8 bCBWCBLength;
37 u8 CBWCB[16];
38} PACKED;
39
40#define CSW_SIGNATURE 0x53425355 // USBS
41
42struct csw_s {
43 u32 dCSWSignature;
44 u32 dCSWTag;
45 u32 dCSWDataResidue;
46 u8 bCSWStatus;
47} PACKED;
48
Paolo Bonzini092fc352011-11-16 13:02:46 +010049static int
50usb_msc_send(struct usbdrive_s *udrive_g, int dir, void *buf, u32 bytes)
51{
52 struct usb_pipe *pipe;
53 if (dir == USB_DIR_OUT)
54 pipe = GET_GLOBAL(udrive_g->bulkout);
55 else
56 pipe = GET_GLOBAL(udrive_g->bulkin);
57 return usb_send_bulk(pipe, dir, buf, bytes);
58}
59
Kevin O'Connor7149fc82010-02-17 23:24:42 -050060// Low-level usb command transmit function.
61int
62usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
63{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -050064 if (!CONFIG_USB_MSC)
65 return 0;
66
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050067 dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n"
Kevin O'Connor7149fc82010-02-17 23:24:42 -050068 , op->drive_g, 0, op->count, blocksize, op->buf_fl);
69 struct usbdrive_s *udrive_g = container_of(
70 op->drive_g, struct usbdrive_s, drive);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050071
72 // Setup command block wrapper.
73 u32 bytes = blocksize * op->count;
74 struct cbw_s cbw;
75 memset(&cbw, 0, sizeof(cbw));
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010076 memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050077 cbw.dCBWSignature = CBW_SIGNATURE;
78 cbw.dCBWTag = 999; // XXX
79 cbw.dCBWDataTransferLength = bytes;
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010080 cbw.bmCBWFlags = (cbw.CBWCB[0] == CDB_CMD_WRITE_10) ? USB_DIR_OUT : USB_DIR_IN;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050081 cbw.bCBWLUN = 0; // XXX
82 cbw.bCBWCBLength = USB_CDB_SIZE;
Kevin O'Connor7149fc82010-02-17 23:24:42 -050083
84 // Transfer cbw to device.
Paolo Bonzini092fc352011-11-16 13:02:46 +010085 int ret = usb_msc_send(udrive_g, USB_DIR_OUT
Kevin O'Connor7149fc82010-02-17 23:24:42 -050086 , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw));
87 if (ret)
88 goto fail;
89
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010090 // Transfer data to/from device.
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010091 if (bytes) {
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +010092 ret = usb_msc_send(udrive_g, cbw.bmCBWFlags, op->buf_fl, bytes);
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +010093 if (ret)
94 goto fail;
95 }
Kevin O'Connor7149fc82010-02-17 23:24:42 -050096
97 // Transfer csw info.
98 struct csw_s csw;
Paolo Bonzini092fc352011-11-16 13:02:46 +010099 ret = usb_msc_send(udrive_g, USB_DIR_IN
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500100 , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
101 if (ret)
102 goto fail;
103
104 if (!csw.bCSWStatus)
105 return DISK_RET_SUCCESS;
106 if (csw.bCSWStatus == 2)
107 goto fail;
108
Paolo Bonzini7f7b0f62011-11-16 13:02:45 +0100109 if (blocksize)
110 op->count -= csw.dCSWDataResidue / blocksize;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500111 return DISK_RET_EBADTRACK;
112
113fail:
114 // XXX - reset connection
115 dprintf(1, "USB transmission failed\n");
116 op->count = 0;
117 return DISK_RET_EBADTRACK;
118}
119
120
121/****************************************************************
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500122 * Setup
123 ****************************************************************/
124
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500125// Configure a usb msc device.
126int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500127usb_msc_init(struct usb_pipe *pipe
128 , struct usb_interface_descriptor *iface, int imax)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500129{
130 if (!CONFIG_USB_MSC)
131 return -1;
132
133 // Verify right kind of device
Kevin O'Connor9c000e62010-09-02 21:18:20 -0400134 if ((iface->bInterfaceSubClass != US_SC_SCSI &&
135 iface->bInterfaceSubClass != US_SC_ATAPI_8070 &&
136 iface->bInterfaceSubClass != US_SC_ATAPI_8020)
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500137 || iface->bInterfaceProtocol != US_PR_BULK) {
138 dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n"
139 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
140 return -1;
141 }
142
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500143 // Allocate drive structure.
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500144 struct usbdrive_s *udrive_g = malloc_fseg(sizeof(*udrive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500145 if (!udrive_g) {
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500146 warn_noalloc();
147 goto fail;
148 }
149 memset(udrive_g, 0, sizeof(*udrive_g));
150 udrive_g->drive.type = DTYPE_USB;
Kevin O'Connor67f6d372010-04-08 20:40:47 -0400151
152 // Find bulk in and bulk out endpoints.
153 struct usb_endpoint_descriptor *indesc = findEndPointDesc(
154 iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_IN);
155 struct usb_endpoint_descriptor *outdesc = findEndPointDesc(
156 iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
157 if (!indesc || !outdesc)
158 goto fail;
159 udrive_g->bulkin = alloc_bulk_pipe(pipe, indesc);
160 udrive_g->bulkout = alloc_bulk_pipe(pipe, outdesc);
161 if (!udrive_g->bulkin || !udrive_g->bulkout)
162 goto fail;
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500163
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500164 int prio = bootprio_find_usb(pipe->cntl->pci, pipe->path);
165 int ret = scsi_init_drive(&udrive_g->drive, "USB MSC", prio);
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500166 if (ret)
167 goto fail;
168
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500169 return 0;
170fail:
171 dprintf(1, "Unable to configure USB MSC device.\n");
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500172 free(udrive_g);
173 return -1;
174}