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