xref: /freebsd/stand/usb/storage/umass_loader.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1 /* $FreeBSD$ */
2 /*-
3  * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
4  * All rights reserved.
5  *
6  * This software was developed by SRI International and the University of
7  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
8  * ("CTSRD"), as part of the DARPA CRASH research programme.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 
34 #include <bootstrap.h>
35 #include <stdarg.h>
36 
37 #include <stand.h>
38 #include <disk.h>
39 
40 #define	HAVE_STANDARD_DEFS
41 
42 #include USB_GLOBAL_INCLUDE_FILE
43 
44 #include "umass_common.h"
45 
46 static int umass_disk_init(void);
47 static int umass_disk_open(struct open_file *,...);
48 static int umass_disk_close(struct open_file *);
49 static void umass_disk_cleanup(void);
50 static int umass_disk_ioctl(struct open_file *, u_long, void *);
51 static int umass_disk_strategy(void *, int, daddr_t, size_t, char *, size_t *);
52 static int umass_disk_print(int);
53 
54 struct devsw umass_disk = {
55 	.dv_name = "umass",
56 	.dv_type = DEVT_DISK,
57 	.dv_init = umass_disk_init,
58 	.dv_strategy = umass_disk_strategy,
59 	.dv_open = umass_disk_open,
60 	.dv_close = umass_disk_close,
61 	.dv_ioctl = umass_disk_ioctl,
62 	.dv_print = umass_disk_print,
63 	.dv_cleanup = umass_disk_cleanup,
64 	.dv_fmtdev = disk_fmtdev,
65 	.dv_parsedev = disk_parsedev,
66 };
67 
68 static int
69 umass_disk_init(void)
70 {
71 	uint32_t time;
72 
73 	usb_init();
74 	usb_needs_explore_all();
75 
76 	/* wait 8 seconds for a USB mass storage device to appear */
77 	for (time = 0; time < (8 * hz); time++) {
78 		usb_idle();
79 		delay(1000000 / hz);
80 		time++;
81 		callout_process(1);
82 		if (umass_uaa.device != NULL)
83 			return (0);
84 	}
85 	return (0);
86 }
87 
88 static int
89 umass_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
90     char *buf, size_t *rsizep)
91 {
92 	if (umass_uaa.device == NULL)
93 		return (ENXIO);
94 	if (rsizep != NULL)
95 		*rsizep = 0;
96 
97 	flag &= F_MASK;
98 	if (flag == F_WRITE) {
99 		if (usb_msc_write_10(umass_uaa.device, 0, dblk, size >> 9, buf) != 0)
100 			return (EINVAL);
101 	} else if (flag == F_READ) {
102 		if (usb_msc_read_10(umass_uaa.device, 0, dblk, size >> 9, buf) != 0)
103 			return (EINVAL);
104 	} else {
105 		return (EROFS);
106 	}
107 
108 	if (rsizep != NULL)
109 		*rsizep = size;
110 	return (0);
111 }
112 
113 static int
114 umass_disk_open_sub(struct disk_devdesc *dev)
115 {
116 	uint32_t nblock;
117 	uint32_t blocksize;
118 
119 	if (usb_msc_read_capacity(umass_uaa.device, 0, &nblock, &blocksize) != 0)
120 		return (EINVAL);
121 
122 	return (disk_open(dev, ((uint64_t)nblock + 1) * (uint64_t)blocksize, blocksize));
123 }
124 
125 static int
126 umass_disk_open(struct open_file *f,...)
127 {
128 	va_list ap;
129 	struct disk_devdesc *dev;
130 
131 	va_start(ap, f);
132 	dev = va_arg(ap, struct disk_devdesc *);
133 	va_end(ap);
134 
135 	if (umass_uaa.device == NULL)
136 		return (ENXIO);
137 	if (dev->d_unit != 0)
138 		return (EIO);
139 	return (umass_disk_open_sub(dev));
140 }
141 
142 static int
143 umass_disk_ioctl(struct open_file *f, u_long cmd, void *buf)
144 {
145 	struct disk_devdesc *dev;
146 	uint32_t nblock;
147 	uint32_t blocksize;
148 	int rc;
149 
150 	dev = (struct disk_devdesc *)(f->f_devdata);
151 	if (dev == NULL)
152 		return (EINVAL);
153 
154 	rc = disk_ioctl(dev, cmd, buf);
155 	if (rc != ENOTTY)
156 		return (rc);
157 
158 	switch (cmd) {
159 	case DIOCGSECTORSIZE:
160 	case DIOCGMEDIASIZE:
161 		if (usb_msc_read_capacity(umass_uaa.device, 0,
162 		    &nblock, &blocksize) != 0)
163 			return (EINVAL);
164 
165 		if (cmd == DIOCGMEDIASIZE)
166 			*(uint64_t*)buf = nblock;
167 		else
168 			*(uint32_t*)buf = blocksize;
169 
170 		return (0);
171 	default:
172 		return (ENXIO);
173 	}
174 }
175 
176 static int
177 umass_disk_close(struct open_file *f)
178 {
179 	struct disk_devdesc *dev;
180 
181 	dev = (struct disk_devdesc *)f->f_devdata;
182 	return (disk_close(dev));
183 }
184 
185 static int
186 umass_disk_print(int verbose)
187 {
188 	struct disk_devdesc dev;
189 
190 	printf("%s devices:", umass_disk.dv_name);
191 	if (pager_output("\n") != 0)
192 		return (1);
193 
194 	memset(&dev, 0, sizeof(dev));
195 
196 	ret = pager_output("    umass0   UMASS device\n");
197 	if (ret != 0)
198 		return (ret);
199 	dev.d_dev = &umass_disk;
200 	dev.d_unit = 0;
201 	dev.d_slice = D_SLICENONE;
202 	dev.d_partition = D_PARTNONE;
203 
204 	if (umass_disk_open_sub(&dev) == 0) {
205 		ret = disk_print(&dev, "    umass0", verbose);
206 		disk_close(&dev);
207 	}
208 	return (ret);
209 }
210 
211 static void
212 umass_disk_cleanup(void)
213 {
214 
215 	usb_uninit();
216 }
217 
218 
219 /* USB specific functions */
220 
221 extern void callout_process(int);
222 extern void usb_idle(void);
223 extern void usb_init(void);
224 extern void usb_uninit(void);
225 
226 void
227 DELAY(unsigned int usdelay)
228 {
229 	delay(usdelay);
230 }
231 
232 int
233 pause(const char *what, int timeout)
234 {
235 	if (timeout == 0)
236 		timeout = 1;
237 
238 	delay((1000000 / hz) * timeout);
239 
240 	return (0);
241 }
242