Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / src / usb-msc.c
1 // 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
14 #include "boot.h" // boot_add_hd
15
16 struct usbdrive_s {
17     struct drive_s drive;
18     struct usb_pipe *bulkin, *bulkout;
19 };
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
30 struct 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
42 struct csw_s {
43     u32 dCSWSignature;
44     u32 dCSWTag;
45     u32 dCSWDataResidue;
46     u8 bCSWStatus;
47 } PACKED;
48
49 // Low-level usb command transmit function.
50 int
51 usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
52 {
53     if (!CONFIG_USB_MSC)
54         return 0;
55
56     dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n"
57             , op->drive_g, 0, op->count, blocksize, op->buf_fl);
58     struct usbdrive_s *udrive_g = container_of(
59         op->drive_g, struct usbdrive_s, drive);
60     struct usb_pipe *bulkin = GET_GLOBAL(udrive_g->bulkin);
61     struct usb_pipe *bulkout = GET_GLOBAL(udrive_g->bulkout);
62
63     // Setup command block wrapper.
64     u32 bytes = blocksize * op->count;
65     struct cbw_s cbw;
66     memset(&cbw, 0, sizeof(cbw));
67     cbw.dCBWSignature = CBW_SIGNATURE;
68     cbw.dCBWTag = 999; // XXX
69     cbw.dCBWDataTransferLength = bytes;
70     cbw.bmCBWFlags = USB_DIR_IN; // XXX
71     cbw.bCBWLUN = 0; // XXX
72     cbw.bCBWCBLength = USB_CDB_SIZE;
73     memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE);
74
75     // Transfer cbw to device.
76     int ret = usb_send_bulk(bulkout, USB_DIR_OUT
77                             , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw));
78     if (ret)
79         goto fail;
80
81     // Transfer data from device.
82     ret = usb_send_bulk(bulkin, USB_DIR_IN, op->buf_fl, bytes);
83     if (ret)
84         goto fail;
85
86     // Transfer csw info.
87     struct csw_s csw;
88     ret = usb_send_bulk(bulkin, USB_DIR_IN
89                         , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
90     if (ret)
91         goto fail;
92
93     if (!csw.bCSWStatus)
94         return DISK_RET_SUCCESS;
95     if (csw.bCSWStatus == 2)
96         goto fail;
97
98     op->count -= csw.dCSWDataResidue / blocksize;
99     return DISK_RET_EBADTRACK;
100
101 fail:
102     // XXX - reset connection
103     dprintf(1, "USB transmission failed\n");
104     op->count = 0;
105     return DISK_RET_EBADTRACK;
106 }
107
108
109 /****************************************************************
110  * Drive ops
111  ****************************************************************/
112
113 // 16bit command demuxer for ATAPI cdroms.
114 int
115 process_usb_op(struct disk_op_s *op)
116 {
117     if (!CONFIG_USB_MSC)
118         return 0;
119     switch (op->command) {
120     case CMD_READ:
121         return cdb_read(op);
122     case CMD_FORMAT:
123     case CMD_WRITE:
124         return DISK_RET_EWRITEPROTECT;
125     case CMD_RESET:
126     case CMD_ISREADY:
127     case CMD_VERIFY:
128     case CMD_SEEK:
129         return DISK_RET_SUCCESS;
130     default:
131         op->count = 0;
132         return DISK_RET_EPARAM;
133     }
134 }
135
136
137 /****************************************************************
138  * Setup
139  ****************************************************************/
140
141 static int
142 setup_drive_cdrom(struct disk_op_s *op, char *desc)
143 {
144     op->drive_g->blksize = CDROM_SECTOR_SIZE;
145     op->drive_g->sectors = (u64)-1;
146     struct usb_pipe *pipe = container_of(
147         op->drive_g, struct usbdrive_s, drive)->bulkout;
148     int prio = bootprio_find_usb(pipe->cntl->pci, pipe->path);
149     boot_add_cd(op->drive_g, desc, prio);
150     return 0;
151 }
152
153 static int
154 setup_drive_hd(struct disk_op_s *op, char *desc)
155 {
156     struct cdbres_read_capacity info;
157     int ret = cdb_read_capacity(op, &info);
158     if (ret)
159         return ret;
160     // XXX - retry for some timeout?
161
162     u32 blksize = ntohl(info.blksize), sectors = ntohl(info.sectors);
163     if (blksize != DISK_SECTOR_SIZE) {
164         if (blksize == CDROM_SECTOR_SIZE)
165             return setup_drive_cdrom(op, desc);
166         dprintf(1, "Unsupported USB MSC block size %d\n", blksize);
167         return -1;
168     }
169     op->drive_g->blksize = blksize;
170     op->drive_g->sectors = sectors;
171     dprintf(1, "USB MSC blksize=%d sectors=%d\n", blksize, sectors);
172
173     // Register with bcv system.
174     struct usb_pipe *pipe = container_of(
175         op->drive_g, struct usbdrive_s, drive)->bulkout;
176     int prio = bootprio_find_usb(pipe->cntl->pci, pipe->path);
177     boot_add_hd(op->drive_g, desc, prio);
178
179     return 0;
180 }
181
182 // Configure a usb msc device.
183 int
184 usb_msc_init(struct usb_pipe *pipe
185              , struct usb_interface_descriptor *iface, int imax)
186 {
187     if (!CONFIG_USB_MSC)
188         return -1;
189
190     // Verify right kind of device
191     if ((iface->bInterfaceSubClass != US_SC_SCSI &&
192          iface->bInterfaceSubClass != US_SC_ATAPI_8070 &&
193          iface->bInterfaceSubClass != US_SC_ATAPI_8020)
194         || iface->bInterfaceProtocol != US_PR_BULK) {
195         dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n"
196                 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
197         return -1;
198     }
199
200     // Allocate drive structure.
201     struct usbdrive_s *udrive_g = malloc_fseg(sizeof(*udrive_g));
202     if (!udrive_g) {
203         warn_noalloc();
204         goto fail;
205     }
206     memset(udrive_g, 0, sizeof(*udrive_g));
207     udrive_g->drive.type = DTYPE_USB;
208
209     // Find bulk in and bulk out endpoints.
210     struct usb_endpoint_descriptor *indesc = findEndPointDesc(
211         iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_IN);
212     struct usb_endpoint_descriptor *outdesc = findEndPointDesc(
213         iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
214     if (!indesc || !outdesc)
215         goto fail;
216     udrive_g->bulkin = alloc_bulk_pipe(pipe, indesc);
217     udrive_g->bulkout = alloc_bulk_pipe(pipe, outdesc);
218     if (!udrive_g->bulkin || !udrive_g->bulkout)
219         goto fail;
220
221     // Validate drive and find block size and sector count.
222     struct disk_op_s dop;
223     memset(&dop, 0, sizeof(dop));
224     dop.drive_g = &udrive_g->drive;
225     struct cdbres_inquiry data;
226     int ret = cdb_get_inquiry(&dop, &data);
227     if (ret)
228         goto fail;
229     char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1];
230     char rev[sizeof(data.rev)+1];
231     strtcpy(vendor, data.vendor, sizeof(vendor));
232     nullTrailingSpace(vendor);
233     strtcpy(product, data.product, sizeof(product));
234     nullTrailingSpace(product);
235     strtcpy(rev, data.rev, sizeof(rev));
236     nullTrailingSpace(rev);
237     int pdt = data.pdt & 0x1f;
238     int removable = !!(data.removable & 0x80);
239     dprintf(1, "USB MSC vendor='%s' product='%s' rev='%s' type=%d removable=%d\n"
240             , vendor, product, rev, pdt, removable);
241     udrive_g->drive.removable = removable;
242
243     if (pdt == USB_MSC_TYPE_CDROM) {
244         char *desc = znprintf(MAXDESCSIZE, "DVD/CD [USB Drive %s %s %s]"
245                               , vendor, product, rev);
246         ret = setup_drive_cdrom(&dop, desc);
247     } else {
248         char *desc = znprintf(MAXDESCSIZE, "USB Drive %s %s %s"
249                               , vendor, product, rev);
250         ret = setup_drive_hd(&dop, desc);
251     }
252     if (ret)
253         goto fail;
254
255     return 0;
256 fail:
257     dprintf(1, "Unable to configure USB MSC device.\n");
258     free(udrive_g);
259     return -1;
260 }