1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc * Minix3 USB mass storage driver implementation
3433d6423SLionel Sambuc * using DDEkit, and libblockdriver
4433d6423SLionel Sambuc */
5433d6423SLionel Sambuc
6*2d64210cSWojciech Zajac #include <sys/cdefs.h> /* __CTASSERT() */
7433d6423SLionel Sambuc #include <sys/ioc_disk.h> /* cases for mass_storage_ioctl */
8433d6423SLionel Sambuc #ifdef USB_STORAGE_SIGNAL
9433d6423SLionel Sambuc #include <sys/signal.h> /* signal handling */
10433d6423SLionel Sambuc #endif
11433d6423SLionel Sambuc
12433d6423SLionel Sambuc #include <ddekit/minix/msg_queue.h>
13433d6423SLionel Sambuc #include <ddekit/thread.h>
14433d6423SLionel Sambuc #include <ddekit/usb.h>
15433d6423SLionel Sambuc
16433d6423SLionel Sambuc #include <minix/blockdriver.h>
17433d6423SLionel Sambuc #include <minix/com.h> /* for msg_queue ranges */
18433d6423SLionel Sambuc #include <minix/drvlib.h> /* DEV_PER_DRIVE, partition */
19433d6423SLionel Sambuc #include <minix/ipc.h> /* message */
20433d6423SLionel Sambuc #include <minix/safecopies.h> /* GRANT_VALID */
21433d6423SLionel Sambuc #include <minix/sef.h>
22433d6423SLionel Sambuc #include <minix/sysutil.h> /* panic */
23433d6423SLionel Sambuc #include <minix/usb.h> /* structures like usb_ctrlrequest */
24433d6423SLionel Sambuc #include <minix/usb_ch9.h> /* descriptor structures */
25433d6423SLionel Sambuc
26433d6423SLionel Sambuc #include <assert.h>
27433d6423SLionel Sambuc #include <limits.h> /* ULONG_MAX */
28*2d64210cSWojciech Zajac #include <time.h> /* nanosleep */
29433d6423SLionel Sambuc
30433d6423SLionel Sambuc #include "common.h"
31433d6423SLionel Sambuc #include "bulk.h"
32433d6423SLionel Sambuc #include "usb_storage.h"
33433d6423SLionel Sambuc #include "urb_helper.h"
34433d6423SLionel Sambuc #include "scsi.h"
35433d6423SLionel Sambuc
36433d6423SLionel Sambuc
37433d6423SLionel Sambuc /*---------------------------*
38433d6423SLionel Sambuc * declared functions *
39433d6423SLionel Sambuc *---------------------------*/
40433d6423SLionel Sambuc /* TODO: these are missing from DDE header files */
41433d6423SLionel Sambuc extern void ddekit_minix_wait_exit(void);
42433d6423SLionel Sambuc extern void ddekit_shutdown(void);
43433d6423SLionel Sambuc
44433d6423SLionel Sambuc /* SCSI URB related prototypes */
45433d6423SLionel Sambuc static int mass_storage_send_scsi_cbw_out(int, scsi_transfer *);
46433d6423SLionel Sambuc static int mass_storage_send_scsi_data_in(void *, unsigned int);
47433d6423SLionel Sambuc static int mass_storage_send_scsi_data_out(void *, unsigned int);
48433d6423SLionel Sambuc static int mass_storage_send_scsi_csw_in(void);
49433d6423SLionel Sambuc
50433d6423SLionel Sambuc #ifdef MASS_RESET_RECOVERY
51433d6423SLionel Sambuc /* Bulk only URB related prototypes */
52433d6423SLionel Sambuc static int mass_storage_reset_recovery(void);
53433d6423SLionel Sambuc static int mass_storage_send_bulk_reset(void);
54433d6423SLionel Sambuc static int mass_storage_send_clear_feature(int, int);
55433d6423SLionel Sambuc #endif
56433d6423SLionel Sambuc
57433d6423SLionel Sambuc /* SEF related functions */
58433d6423SLionel Sambuc static int mass_storage_sef_hdlr(int, sef_init_info_t *);
59433d6423SLionel Sambuc static void mass_storage_signal_handler(int);
60433d6423SLionel Sambuc
61433d6423SLionel Sambuc /* DDEKit IPC related */
62433d6423SLionel Sambuc static void ddekit_usb_task(void *);
63433d6423SLionel Sambuc
64433d6423SLionel Sambuc /* Mass storage related prototypes */
65433d6423SLionel Sambuc static void mass_storage_task(void *);
66433d6423SLionel Sambuc static int mass_storage_test(void);
67*2d64210cSWojciech Zajac static int mass_storage_check_error(void);
68433d6423SLionel Sambuc static int mass_storage_try_first_open(void);
69433d6423SLionel Sambuc static int mass_storage_transfer_restrictions(u64_t, unsigned long);
70433d6423SLionel Sambuc static ssize_t mass_storage_write(unsigned long, endpoint_t, iovec_t *,
71433d6423SLionel Sambuc unsigned int, unsigned long);
72433d6423SLionel Sambuc static ssize_t mass_storage_read(unsigned long, endpoint_t, iovec_t *,
73433d6423SLionel Sambuc unsigned int, unsigned long);
74433d6423SLionel Sambuc
75433d6423SLionel Sambuc /* Minix's libblockdriver callbacks */
76433d6423SLionel Sambuc static int mass_storage_open(devminor_t, int);
77433d6423SLionel Sambuc static int mass_storage_close(devminor_t);
78433d6423SLionel Sambuc static ssize_t mass_storage_transfer(devminor_t, int, u64_t, endpoint_t,
79433d6423SLionel Sambuc iovec_t *, unsigned int, int);
80433d6423SLionel Sambuc static int mass_storage_ioctl(devminor_t, unsigned long, endpoint_t,
81433d6423SLionel Sambuc cp_grant_id_t, endpoint_t);
82433d6423SLionel Sambuc static void mass_storage_cleanup(void);
83433d6423SLionel Sambuc static struct device * mass_storage_part(devminor_t);
84433d6423SLionel Sambuc static void mass_storage_geometry(devminor_t, struct part_geom *);
85433d6423SLionel Sambuc
86433d6423SLionel Sambuc /* DDEKit's USB driver callbacks */
87433d6423SLionel Sambuc static void usb_driver_completion(void *);
88433d6423SLionel Sambuc static void usb_driver_connect(struct ddekit_usb_dev *, unsigned int);
89433d6423SLionel Sambuc static void usb_driver_disconnect(struct ddekit_usb_dev *);
90433d6423SLionel Sambuc
91433d6423SLionel Sambuc /* Simplified enumeration method for endpoint resolution */
92433d6423SLionel Sambuc static int mass_storage_get_endpoints(urb_ep_config *, urb_ep_config *);
93433d6423SLionel Sambuc static int mass_storage_parse_endpoint(usb_descriptor_t *, urb_ep_config *,
94433d6423SLionel Sambuc urb_ep_config *);
95433d6423SLionel Sambuc static int mass_storage_parse_descriptors(char *, unsigned int, urb_ep_config *,
96433d6423SLionel Sambuc urb_ep_config *);
97433d6423SLionel Sambuc
98433d6423SLionel Sambuc
99433d6423SLionel Sambuc /*---------------------------*
100433d6423SLionel Sambuc * defined variables *
101433d6423SLionel Sambuc *---------------------------*/
102*2d64210cSWojciech Zajac #define MASS_PACKED __attribute__((__packed__))
103*2d64210cSWojciech Zajac
104433d6423SLionel Sambuc /* Mass Storage callback structure */
105433d6423SLionel Sambuc static struct blockdriver mass_storage = {
106433d6423SLionel Sambuc .bdr_type = BLOCKDRIVER_TYPE_DISK,
107433d6423SLionel Sambuc .bdr_open = mass_storage_open,
108433d6423SLionel Sambuc .bdr_close = mass_storage_close,
109433d6423SLionel Sambuc .bdr_transfer = mass_storage_transfer,
110433d6423SLionel Sambuc .bdr_ioctl = mass_storage_ioctl,
111433d6423SLionel Sambuc .bdr_cleanup = mass_storage_cleanup,
112433d6423SLionel Sambuc .bdr_part = mass_storage_part,
113433d6423SLionel Sambuc .bdr_geometry = mass_storage_geometry,
114433d6423SLionel Sambuc .bdr_intr = NULL,
115433d6423SLionel Sambuc .bdr_alarm = NULL,
116433d6423SLionel Sambuc .bdr_other = NULL,
117433d6423SLionel Sambuc .bdr_device = NULL
118433d6423SLionel Sambuc };
119433d6423SLionel Sambuc
120433d6423SLionel Sambuc /* USB callback structure */
121433d6423SLionel Sambuc static struct ddekit_usb_driver mass_storage_driver = {
122433d6423SLionel Sambuc .completion = usb_driver_completion,
123433d6423SLionel Sambuc .connect = usb_driver_connect,
124433d6423SLionel Sambuc .disconnect = usb_driver_disconnect
125433d6423SLionel Sambuc };
126433d6423SLionel Sambuc
127433d6423SLionel Sambuc /* Instance of global driver information */
128433d6423SLionel Sambuc mass_storage_state driver_state;
129433d6423SLionel Sambuc
130433d6423SLionel Sambuc /* Tags used to pair CBW and CSW for bulk communication
131433d6423SLionel Sambuc * With this we can check if SCSI reply was meant for SCSI request */
132433d6423SLionel Sambuc static unsigned int current_cbw_tag = 0; /* What shall be send next */
133433d6423SLionel Sambuc static unsigned int last_cbw_tag = 0; /* What was sent recently */
134433d6423SLionel Sambuc
135433d6423SLionel Sambuc /* Semaphore used to block mass storage thread to
136433d6423SLionel Sambuc * allow DDE dispatcher operation */
137433d6423SLionel Sambuc static ddekit_sem_t * mass_storage_sem = NULL;
138433d6423SLionel Sambuc
139433d6423SLionel Sambuc /* Mass storage (using libblockdriver) thread */
140433d6423SLionel Sambuc ddekit_thread_t * mass_storage_thread = NULL;
141433d6423SLionel Sambuc
142433d6423SLionel Sambuc /* DDEKit USB message handling thread */
143433d6423SLionel Sambuc ddekit_thread_t * ddekit_usb_thread = NULL;
144433d6423SLionel Sambuc
145433d6423SLionel Sambuc /* Static URB buffer size (must be multiple of SECTOR_SIZE) */
146433d6423SLionel Sambuc #define BUFFER_SIZE (64*SECTOR_SIZE)
147433d6423SLionel Sambuc
148433d6423SLionel Sambuc /* Large buffer for URB read/write operations */
149433d6423SLionel Sambuc static unsigned char buffer[BUFFER_SIZE];
150433d6423SLionel Sambuc
151433d6423SLionel Sambuc /* Length of local buffer where descriptors are temporarily stored */
152433d6423SLionel Sambuc #define MAX_DESCRIPTORS_LEN 128
153433d6423SLionel Sambuc
154433d6423SLionel Sambuc /* Maximum 'Test Unit Ready' command retries */
155433d6423SLionel Sambuc #define MAX_TEST_RETRIES 3
156433d6423SLionel Sambuc
157*2d64210cSWojciech Zajac /* 'Test Unit Ready' failure delay time (in nanoseconds) */
158*2d64210cSWojciech Zajac #define NEXT_TEST_DELAY 50000000 /* 50ms */
159*2d64210cSWojciech Zajac
160*2d64210cSWojciech Zajac
161433d6423SLionel Sambuc /*---------------------------*
162433d6423SLionel Sambuc * defined functions *
163433d6423SLionel Sambuc *---------------------------*/
164433d6423SLionel Sambuc /*===========================================================================*
165433d6423SLionel Sambuc * main *
166433d6423SLionel Sambuc *===========================================================================*/
167433d6423SLionel Sambuc int
main(int argc,char * argv[])168433d6423SLionel Sambuc main(int argc, char * argv[])
169433d6423SLionel Sambuc {
170433d6423SLionel Sambuc MASS_DEBUG_MSG("Starting...");
171433d6423SLionel Sambuc
172433d6423SLionel Sambuc /* Store arguments for future parsing */
173433d6423SLionel Sambuc env_setargs(argc, argv);
174433d6423SLionel Sambuc
175433d6423SLionel Sambuc /* Clear current state */
176433d6423SLionel Sambuc memset(&driver_state, 0, sizeof(driver_state));
177433d6423SLionel Sambuc
178433d6423SLionel Sambuc /* Initialize SEF related callbacks */
179433d6423SLionel Sambuc sef_setcb_init_fresh(mass_storage_sef_hdlr);
180433d6423SLionel Sambuc sef_setcb_init_lu(mass_storage_sef_hdlr);
181433d6423SLionel Sambuc sef_setcb_init_restart(mass_storage_sef_hdlr);
182433d6423SLionel Sambuc sef_setcb_signal_handler(mass_storage_signal_handler);
183433d6423SLionel Sambuc
184433d6423SLionel Sambuc /* Initialize DDEkit (involves sef_startup()) */
185433d6423SLionel Sambuc ddekit_init();
186433d6423SLionel Sambuc MASS_DEBUG_MSG("DDEkit ready...");
187433d6423SLionel Sambuc
188433d6423SLionel Sambuc /* Semaphore initialization */
189433d6423SLionel Sambuc mass_storage_sem = ddekit_sem_init(0);
190433d6423SLionel Sambuc if (NULL == mass_storage_sem)
191433d6423SLionel Sambuc panic("Initializing mass_storage_sem failed!");
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc /* Starting mass storage thread */
194433d6423SLionel Sambuc mass_storage_thread = ddekit_thread_create(mass_storage_task, NULL,
195433d6423SLionel Sambuc "mass_storage_task");
196433d6423SLionel Sambuc if (NULL == mass_storage_thread)
197433d6423SLionel Sambuc panic("Initializing mass_storage_thread failed!");
198433d6423SLionel Sambuc
199433d6423SLionel Sambuc MASS_DEBUG_MSG("Storage task (libblockdriver) ready...");
200433d6423SLionel Sambuc
201433d6423SLionel Sambuc /* Run USB IPC task to collect messages */
202433d6423SLionel Sambuc ddekit_usb_thread = ddekit_thread_create(ddekit_usb_task, NULL,
203433d6423SLionel Sambuc "ddekit_task" );
204433d6423SLionel Sambuc if (NULL == ddekit_usb_thread)
205433d6423SLionel Sambuc panic("Initializing ddekit_usb_thread failed!");
206433d6423SLionel Sambuc
207433d6423SLionel Sambuc MASS_DEBUG_MSG("USB IPC task ready...");
208433d6423SLionel Sambuc
209433d6423SLionel Sambuc /* Block and wait until exit signal is received */
210433d6423SLionel Sambuc ddekit_minix_wait_exit();
211433d6423SLionel Sambuc MASS_DEBUG_MSG("Exiting...");
212433d6423SLionel Sambuc
213433d6423SLionel Sambuc /* Release objects that were explicitly allocated above */
214433d6423SLionel Sambuc ddekit_thread_terminate(ddekit_usb_thread);
215433d6423SLionel Sambuc ddekit_thread_terminate(mass_storage_thread);
216433d6423SLionel Sambuc ddekit_sem_deinit(mass_storage_sem);
217433d6423SLionel Sambuc
218433d6423SLionel Sambuc /* TODO: no ddekit_deinit for proper cleanup? */
219433d6423SLionel Sambuc
220433d6423SLionel Sambuc MASS_DEBUG_MSG("Cleanup completed...");
221433d6423SLionel Sambuc
222433d6423SLionel Sambuc return EXIT_SUCCESS;
223433d6423SLionel Sambuc }
224433d6423SLionel Sambuc
225433d6423SLionel Sambuc
226433d6423SLionel Sambuc /*===========================================================================*
227433d6423SLionel Sambuc * mass_storage_send_scsi_cbw_out *
228433d6423SLionel Sambuc *===========================================================================*/
229433d6423SLionel Sambuc static int
mass_storage_send_scsi_cbw_out(int scsi_cmd,scsi_transfer * info)230433d6423SLionel Sambuc mass_storage_send_scsi_cbw_out(int scsi_cmd, scsi_transfer * info)
231433d6423SLionel Sambuc {
232433d6423SLionel Sambuc /* URB to be send */
233433d6423SLionel Sambuc struct ddekit_usb_urb urb;
234433d6423SLionel Sambuc
235433d6423SLionel Sambuc /* CBW data buffer */
236433d6423SLionel Sambuc mass_storage_cbw cbw;
237433d6423SLionel Sambuc
238433d6423SLionel Sambuc MASS_DEBUG_DUMP;
239433d6423SLionel Sambuc
240433d6423SLionel Sambuc /* Reset URB and assign given values */
241433d6423SLionel Sambuc init_urb(&urb, driver_state.cur_periph->dev,
242433d6423SLionel Sambuc &(driver_state.cur_periph->ep_out));
243433d6423SLionel Sambuc
244433d6423SLionel Sambuc /* Reset CBW and assign default values */
245433d6423SLionel Sambuc init_cbw(&cbw, last_cbw_tag = current_cbw_tag++);
246433d6423SLionel Sambuc
247433d6423SLionel Sambuc /* Fill CBW with SCSI command */
248433d6423SLionel Sambuc if (create_scsi_cmd(&cbw, scsi_cmd, info))
249433d6423SLionel Sambuc return EXIT_FAILURE;
250433d6423SLionel Sambuc
251433d6423SLionel Sambuc /* Attach CBW to URB */
252433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_DATA, &cbw, sizeof(cbw));
253433d6423SLionel Sambuc
254433d6423SLionel Sambuc /* Send and wait for response */
255433d6423SLionel Sambuc if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
256433d6423SLionel Sambuc return EXIT_FAILURE;
257433d6423SLionel Sambuc
258433d6423SLionel Sambuc return EXIT_SUCCESS;
259433d6423SLionel Sambuc }
260433d6423SLionel Sambuc
261433d6423SLionel Sambuc
262433d6423SLionel Sambuc /*===========================================================================*
263433d6423SLionel Sambuc * mass_storage_send_scsi_data_in *
264433d6423SLionel Sambuc *===========================================================================*/
265433d6423SLionel Sambuc static int
mass_storage_send_scsi_data_in(void * buf,unsigned int in_len)266433d6423SLionel Sambuc mass_storage_send_scsi_data_in(void * buf, unsigned int in_len)
267433d6423SLionel Sambuc {
268433d6423SLionel Sambuc /* URB to be send */
269433d6423SLionel Sambuc struct ddekit_usb_urb urb;
270433d6423SLionel Sambuc
271433d6423SLionel Sambuc MASS_DEBUG_DUMP;
272433d6423SLionel Sambuc
273433d6423SLionel Sambuc /* Reset URB and assign given values */
274433d6423SLionel Sambuc init_urb(&urb, driver_state.cur_periph->dev,
275433d6423SLionel Sambuc &(driver_state.cur_periph->ep_in));
276433d6423SLionel Sambuc
277433d6423SLionel Sambuc /* Attach buffer to URB */
278433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_DATA, buf, in_len);
279433d6423SLionel Sambuc
280433d6423SLionel Sambuc /* Send and wait for response */
281433d6423SLionel Sambuc if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
282433d6423SLionel Sambuc return EXIT_FAILURE;
283433d6423SLionel Sambuc
284433d6423SLionel Sambuc return EXIT_SUCCESS;
285433d6423SLionel Sambuc }
286433d6423SLionel Sambuc
287433d6423SLionel Sambuc
288433d6423SLionel Sambuc /*===========================================================================*
289433d6423SLionel Sambuc * mass_storage_send_scsi_data_out *
290433d6423SLionel Sambuc *===========================================================================*/
291433d6423SLionel Sambuc static int
mass_storage_send_scsi_data_out(void * buf,unsigned int out_len)292433d6423SLionel Sambuc mass_storage_send_scsi_data_out(void * buf, unsigned int out_len)
293433d6423SLionel Sambuc {
294433d6423SLionel Sambuc /* URB to be send */
295433d6423SLionel Sambuc struct ddekit_usb_urb urb;
296433d6423SLionel Sambuc
297433d6423SLionel Sambuc MASS_DEBUG_DUMP;
298433d6423SLionel Sambuc
299433d6423SLionel Sambuc /* Reset URB and assign given values */
300433d6423SLionel Sambuc init_urb(&urb, driver_state.cur_periph->dev,
301433d6423SLionel Sambuc &(driver_state.cur_periph->ep_out));
302433d6423SLionel Sambuc
303433d6423SLionel Sambuc /* Attach buffer to URB */
304433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_DATA, buf, out_len);
305433d6423SLionel Sambuc
306433d6423SLionel Sambuc /* Send and wait for response */
307433d6423SLionel Sambuc if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
308433d6423SLionel Sambuc return EXIT_FAILURE;
309433d6423SLionel Sambuc
310433d6423SLionel Sambuc return EXIT_SUCCESS;
311433d6423SLionel Sambuc }
312433d6423SLionel Sambuc
313433d6423SLionel Sambuc
314433d6423SLionel Sambuc /*===========================================================================*
315433d6423SLionel Sambuc * mass_storage_send_scsi_csw_in *
316433d6423SLionel Sambuc *===========================================================================*/
317433d6423SLionel Sambuc static int
mass_storage_send_scsi_csw_in(void)318433d6423SLionel Sambuc mass_storage_send_scsi_csw_in(void)
319433d6423SLionel Sambuc {
320433d6423SLionel Sambuc /* URB to be send */
321433d6423SLionel Sambuc struct ddekit_usb_urb urb;
322433d6423SLionel Sambuc
323433d6423SLionel Sambuc /* CBW data buffer */
324433d6423SLionel Sambuc mass_storage_csw csw;
325433d6423SLionel Sambuc
326433d6423SLionel Sambuc MASS_DEBUG_DUMP;
327433d6423SLionel Sambuc
328433d6423SLionel Sambuc /* Reset URB and assign given values */
329433d6423SLionel Sambuc init_urb(&urb, driver_state.cur_periph->dev,
330433d6423SLionel Sambuc &(driver_state.cur_periph->ep_in));
331433d6423SLionel Sambuc
332433d6423SLionel Sambuc /* Clear CSW for receiving */
333433d6423SLionel Sambuc init_csw(&csw);
334433d6423SLionel Sambuc
335433d6423SLionel Sambuc /* Attach CSW to URB */
336433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_DATA, &csw, sizeof(csw));
337433d6423SLionel Sambuc
338433d6423SLionel Sambuc /* Send and wait for response */
339433d6423SLionel Sambuc if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
340433d6423SLionel Sambuc return EXIT_FAILURE;
341433d6423SLionel Sambuc
342433d6423SLionel Sambuc /* Check for proper reply */
343433d6423SLionel Sambuc if (check_csw(&csw, last_cbw_tag))
344433d6423SLionel Sambuc return EXIT_FAILURE;
345433d6423SLionel Sambuc
346433d6423SLionel Sambuc return EXIT_SUCCESS;
347433d6423SLionel Sambuc }
348433d6423SLionel Sambuc
349433d6423SLionel Sambuc
350433d6423SLionel Sambuc #ifdef MASS_RESET_RECOVERY
351433d6423SLionel Sambuc /*===========================================================================*
352433d6423SLionel Sambuc * mass_storage_reset_recovery *
353433d6423SLionel Sambuc *===========================================================================*/
354433d6423SLionel Sambuc static int
mass_storage_reset_recovery(void)355433d6423SLionel Sambuc mass_storage_reset_recovery(void)
356433d6423SLionel Sambuc {
357433d6423SLionel Sambuc MASS_DEBUG_DUMP;
358433d6423SLionel Sambuc
359433d6423SLionel Sambuc /* Bulk-Only Mass Storage Reset */
360433d6423SLionel Sambuc if (mass_storage_send_bulk_reset()) {
361433d6423SLionel Sambuc MASS_MSG("Bulk-only mass storage reset failed");
362433d6423SLionel Sambuc return EIO;
363433d6423SLionel Sambuc }
364433d6423SLionel Sambuc
365433d6423SLionel Sambuc /* Clear Feature HALT to the Bulk-In endpoint */
366433d6423SLionel Sambuc if (URB_INVALID_EP != driver_state.cur_periph->ep_in.ep_num)
367433d6423SLionel Sambuc if (mass_storage_send_clear_feature(
368433d6423SLionel Sambuc driver_state.cur_periph->ep_in.ep_num,
369433d6423SLionel Sambuc DDEKIT_USB_IN)) {
370433d6423SLionel Sambuc MASS_MSG("Resetting IN EP failed");
371433d6423SLionel Sambuc return EIO;
372433d6423SLionel Sambuc }
373433d6423SLionel Sambuc
374433d6423SLionel Sambuc /* Clear Feature HALT to the Bulk-Out endpoint */
375433d6423SLionel Sambuc if (URB_INVALID_EP != driver_state.cur_periph->ep_out.ep_num)
376433d6423SLionel Sambuc if (mass_storage_send_clear_feature(
377433d6423SLionel Sambuc driver_state.cur_periph->ep_out.ep_num,
378433d6423SLionel Sambuc DDEKIT_USB_OUT)) {
379433d6423SLionel Sambuc MASS_MSG("Resetting OUT EP failed");
380433d6423SLionel Sambuc return EIO;
381433d6423SLionel Sambuc }
382433d6423SLionel Sambuc
383433d6423SLionel Sambuc return EXIT_SUCCESS;
384433d6423SLionel Sambuc }
385433d6423SLionel Sambuc
386433d6423SLionel Sambuc
387433d6423SLionel Sambuc /*===========================================================================*
388433d6423SLionel Sambuc * mass_storage_send_bulk_reset *
389433d6423SLionel Sambuc *===========================================================================*/
390433d6423SLionel Sambuc static int
mass_storage_send_bulk_reset(void)391433d6423SLionel Sambuc mass_storage_send_bulk_reset(void)
392433d6423SLionel Sambuc {
393433d6423SLionel Sambuc /* URB to be send */
394433d6423SLionel Sambuc struct ddekit_usb_urb urb;
395433d6423SLionel Sambuc
396433d6423SLionel Sambuc /* Setup buffer to be send */
397433d6423SLionel Sambuc struct usb_ctrlrequest bulk_setup;
398433d6423SLionel Sambuc
399433d6423SLionel Sambuc /* Control EP configuration */
400433d6423SLionel Sambuc urb_ep_config ep_conf;
401433d6423SLionel Sambuc
402433d6423SLionel Sambuc MASS_DEBUG_DUMP;
403433d6423SLionel Sambuc
404433d6423SLionel Sambuc /* Initialize EP configuration */
405433d6423SLionel Sambuc ep_conf.ep_num = 0;
406433d6423SLionel Sambuc ep_conf.direction = DDEKIT_USB_OUT;
407433d6423SLionel Sambuc ep_conf.type = DDEKIT_USB_TRANSFER_CTL;
408433d6423SLionel Sambuc ep_conf.max_packet_size = 0;
409433d6423SLionel Sambuc ep_conf.interval = 0;
410433d6423SLionel Sambuc
411433d6423SLionel Sambuc /* Reset URB and assign given values */
412433d6423SLionel Sambuc init_urb(&urb, driver_state.cur_periph->dev, &ep_conf);
413433d6423SLionel Sambuc
414433d6423SLionel Sambuc /* Clear setup data */
415433d6423SLionel Sambuc memset(&bulk_setup, 0, sizeof(bulk_setup));
416433d6423SLionel Sambuc
417433d6423SLionel Sambuc /* For explanation of these values see usbmassbulk_10.pdf */
418433d6423SLionel Sambuc /* 3.1 Bulk-Only Mass Storage Reset */
419433d6423SLionel Sambuc bulk_setup.bRequestType = 0x21; /* Class, Interface, host to device */
420433d6423SLionel Sambuc bulk_setup.bRequest = 0xff;
421433d6423SLionel Sambuc bulk_setup.wValue = 0x00;
422433d6423SLionel Sambuc bulk_setup.wIndex = 0x00; /* TODO: hard-coded interface 0 */
423433d6423SLionel Sambuc bulk_setup.wLength = 0x00;
424433d6423SLionel Sambuc
425433d6423SLionel Sambuc /* Attach request to URB */
426433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_SETUP,
427433d6423SLionel Sambuc &bulk_setup, sizeof(bulk_setup));
428433d6423SLionel Sambuc
429433d6423SLionel Sambuc /* Send and wait for response */
430433d6423SLionel Sambuc if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
431433d6423SLionel Sambuc return EXIT_FAILURE;
432433d6423SLionel Sambuc
433433d6423SLionel Sambuc return EXIT_SUCCESS;
434433d6423SLionel Sambuc }
435433d6423SLionel Sambuc
436433d6423SLionel Sambuc
437433d6423SLionel Sambuc /*===========================================================================*
438433d6423SLionel Sambuc * mass_storage_send_clear_feature *
439433d6423SLionel Sambuc *===========================================================================*/
440433d6423SLionel Sambuc static int
mass_storage_send_clear_feature(int ep_num,int direction)441433d6423SLionel Sambuc mass_storage_send_clear_feature(int ep_num, int direction)
442433d6423SLionel Sambuc {
443433d6423SLionel Sambuc /* URB to be send */
444433d6423SLionel Sambuc struct ddekit_usb_urb urb;
445433d6423SLionel Sambuc
446433d6423SLionel Sambuc /* Setup buffer to be send */
447433d6423SLionel Sambuc struct usb_ctrlrequest bulk_setup;
448433d6423SLionel Sambuc
449433d6423SLionel Sambuc /* Control EP configuration */
450433d6423SLionel Sambuc urb_ep_config ep_conf;
451433d6423SLionel Sambuc
452433d6423SLionel Sambuc MASS_DEBUG_DUMP;
453433d6423SLionel Sambuc
454433d6423SLionel Sambuc assert((ep_num >= 0) && (ep_num < 16));
455433d6423SLionel Sambuc assert((DDEKIT_USB_OUT == direction) || (DDEKIT_USB_IN == direction));
456433d6423SLionel Sambuc
457433d6423SLionel Sambuc /* Initialize EP configuration */
458433d6423SLionel Sambuc ep_conf.ep_num = 0;
459433d6423SLionel Sambuc ep_conf.direction = DDEKIT_USB_OUT;
460433d6423SLionel Sambuc ep_conf.type = DDEKIT_USB_TRANSFER_CTL;
461433d6423SLionel Sambuc ep_conf.max_packet_size = 0;
462433d6423SLionel Sambuc ep_conf.interval = 0;
463433d6423SLionel Sambuc
464433d6423SLionel Sambuc /* Reset URB and assign given values */
465433d6423SLionel Sambuc init_urb(&urb, driver_state.cur_periph->dev, &ep_conf);
466433d6423SLionel Sambuc
467433d6423SLionel Sambuc /* Clear setup data */
468433d6423SLionel Sambuc memset(&bulk_setup, 0, sizeof(bulk_setup));
469433d6423SLionel Sambuc
470433d6423SLionel Sambuc /* For explanation of these values see usbmassbulk_10.pdf */
471433d6423SLionel Sambuc /* 3.1 Bulk-Only Mass Storage Reset */
472433d6423SLionel Sambuc bulk_setup.bRequestType = 0x02; /* Standard, Endpoint, host to device */
473433d6423SLionel Sambuc bulk_setup.bRequest = 0x01; /* CLEAR_FEATURE */
474433d6423SLionel Sambuc bulk_setup.wValue = 0x00; /* Endpoint */
475433d6423SLionel Sambuc bulk_setup.wIndex = ep_num; /* Endpoint number... */
476433d6423SLionel Sambuc if (DDEKIT_USB_IN == direction)
477433d6423SLionel Sambuc bulk_setup.wIndex |= UE_DIR_IN; /* ...and direction bit */
478433d6423SLionel Sambuc bulk_setup.wLength = 0x00;
479433d6423SLionel Sambuc
480433d6423SLionel Sambuc /* Attach request to URB */
481433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_SETUP,
482433d6423SLionel Sambuc &bulk_setup, sizeof(bulk_setup));
483433d6423SLionel Sambuc
484433d6423SLionel Sambuc /* Send and wait for response */
485433d6423SLionel Sambuc if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
486433d6423SLionel Sambuc return EXIT_FAILURE;
487433d6423SLionel Sambuc
488433d6423SLionel Sambuc return EXIT_SUCCESS;
489433d6423SLionel Sambuc }
490433d6423SLionel Sambuc #endif
491433d6423SLionel Sambuc
492433d6423SLionel Sambuc
493433d6423SLionel Sambuc /*===========================================================================*
494433d6423SLionel Sambuc * mass_storage_sef_hdlr *
495433d6423SLionel Sambuc *===========================================================================*/
496433d6423SLionel Sambuc static int
mass_storage_sef_hdlr(int type,sef_init_info_t * UNUSED (info))497433d6423SLionel Sambuc mass_storage_sef_hdlr(int type, sef_init_info_t * UNUSED(info))
498433d6423SLionel Sambuc {
499433d6423SLionel Sambuc int env_res;
500433d6423SLionel Sambuc
501433d6423SLionel Sambuc MASS_DEBUG_DUMP;
502433d6423SLionel Sambuc
503433d6423SLionel Sambuc /* Parse given environment */
504433d6423SLionel Sambuc env_res = env_parse("instance", "d", 0,
505433d6423SLionel Sambuc &(driver_state.instance),0, 255);
506433d6423SLionel Sambuc
507433d6423SLionel Sambuc /* Get instance number */
508433d6423SLionel Sambuc if (EP_UNSET == env_res) {
509433d6423SLionel Sambuc MASS_DEBUG_MSG("Instance number was not supplied");
510433d6423SLionel Sambuc driver_state.instance = 0;
511433d6423SLionel Sambuc } else {
512433d6423SLionel Sambuc /* Only SET and UNSET are allowed */
513433d6423SLionel Sambuc if (EP_SET != env_res)
514433d6423SLionel Sambuc return EXIT_FAILURE;
515433d6423SLionel Sambuc }
516433d6423SLionel Sambuc
517433d6423SLionel Sambuc switch (type) {
518433d6423SLionel Sambuc case SEF_INIT_FRESH:
519433d6423SLionel Sambuc /* Announce we are up! */
520433d6423SLionel Sambuc blockdriver_announce(type);
521433d6423SLionel Sambuc return EXIT_SUCCESS;
522433d6423SLionel Sambuc case SEF_INIT_LU:
523433d6423SLionel Sambuc case SEF_INIT_RESTART:
524433d6423SLionel Sambuc MASS_MSG("Only 'fresh' SEF initialization supported\n");
525433d6423SLionel Sambuc break;
526433d6423SLionel Sambuc default:
527433d6423SLionel Sambuc MASS_MSG("illegal SEF type\n");
528433d6423SLionel Sambuc break;
529433d6423SLionel Sambuc }
530433d6423SLionel Sambuc
531433d6423SLionel Sambuc return EXIT_FAILURE;
532433d6423SLionel Sambuc }
533433d6423SLionel Sambuc
534433d6423SLionel Sambuc
535433d6423SLionel Sambuc /*===========================================================================*
536433d6423SLionel Sambuc * mass_storage_signal_handler *
537433d6423SLionel Sambuc *===========================================================================*/
538433d6423SLionel Sambuc static void
mass_storage_signal_handler(int this_signal)539433d6423SLionel Sambuc mass_storage_signal_handler(int this_signal)
540433d6423SLionel Sambuc {
541433d6423SLionel Sambuc MASS_DEBUG_DUMP;
542433d6423SLionel Sambuc
543433d6423SLionel Sambuc #ifdef USB_STORAGE_SIGNAL
544433d6423SLionel Sambuc /* Only check for termination signal, ignore anything else. */
545433d6423SLionel Sambuc if (this_signal != SIGTERM)
546433d6423SLionel Sambuc return;
547433d6423SLionel Sambuc #else
548433d6423SLionel Sambuc MASS_MSG("Handling signal 0x%X", this_signal);
549433d6423SLionel Sambuc #endif
550433d6423SLionel Sambuc
551433d6423SLionel Sambuc /* Try graceful DDEKit exit */
552433d6423SLionel Sambuc ddekit_shutdown();
553433d6423SLionel Sambuc
554433d6423SLionel Sambuc /* Unreachable, when ddekit_shutdown works correctly */
555433d6423SLionel Sambuc panic("Calling ddekit_shutdown failed!");
556433d6423SLionel Sambuc }
557433d6423SLionel Sambuc
558433d6423SLionel Sambuc
559433d6423SLionel Sambuc /*===========================================================================*
560433d6423SLionel Sambuc * ddekit_usb_task *
561433d6423SLionel Sambuc *===========================================================================*/
562433d6423SLionel Sambuc static void
ddekit_usb_task(void * UNUSED (arg))563433d6423SLionel Sambuc ddekit_usb_task(void * UNUSED(arg))
564433d6423SLionel Sambuc {
565433d6423SLionel Sambuc MASS_DEBUG_DUMP;
566433d6423SLionel Sambuc
567433d6423SLionel Sambuc /* TODO: This call was meant to return 'int' but loops forever instead,
568433d6423SLionel Sambuc * so no return value is checked */
569433d6423SLionel Sambuc ddekit_usb_init(&mass_storage_driver, NULL, NULL);
570433d6423SLionel Sambuc }
571433d6423SLionel Sambuc
572433d6423SLionel Sambuc
573433d6423SLionel Sambuc /*===========================================================================*
574433d6423SLionel Sambuc * mass_storage_task *
575433d6423SLionel Sambuc *===========================================================================*/
576433d6423SLionel Sambuc static void
mass_storage_task(void * UNUSED (unused))577433d6423SLionel Sambuc mass_storage_task(void * UNUSED(unused))
578433d6423SLionel Sambuc {
579433d6423SLionel Sambuc message m;
580433d6423SLionel Sambuc int ipc_status;
581433d6423SLionel Sambuc struct ddekit_minix_msg_q * mq;
582433d6423SLionel Sambuc
583433d6423SLionel Sambuc MASS_DEBUG_DUMP;
584433d6423SLionel Sambuc
585433d6423SLionel Sambuc mq = ddekit_minix_create_msg_q(BDEV_RQ_BASE, BDEV_RQ_BASE + 0xff);
586433d6423SLionel Sambuc
587433d6423SLionel Sambuc for (;;) {
588433d6423SLionel Sambuc ddekit_minix_rcv(mq, &m, &ipc_status);
589433d6423SLionel Sambuc blockdriver_process(&mass_storage, &m, ipc_status);
590433d6423SLionel Sambuc }
591433d6423SLionel Sambuc }
592433d6423SLionel Sambuc
593433d6423SLionel Sambuc
594433d6423SLionel Sambuc /*===========================================================================*
595433d6423SLionel Sambuc * mass_storage_test *
596433d6423SLionel Sambuc *===========================================================================*/
597433d6423SLionel Sambuc static int
mass_storage_test(void)598433d6423SLionel Sambuc mass_storage_test(void)
599433d6423SLionel Sambuc {
600433d6423SLionel Sambuc int repeat;
601*2d64210cSWojciech Zajac int error;
602*2d64210cSWojciech Zajac
603*2d64210cSWojciech Zajac struct timespec test_wait;
604433d6423SLionel Sambuc
605433d6423SLionel Sambuc MASS_DEBUG_DUMP;
606433d6423SLionel Sambuc
607*2d64210cSWojciech Zajac /* Delay between consecutive test commands, in case of their failure */
608*2d64210cSWojciech Zajac test_wait.tv_nsec = NEXT_TEST_DELAY;
609*2d64210cSWojciech Zajac test_wait.tv_sec = 0;
610*2d64210cSWojciech Zajac
611433d6423SLionel Sambuc for (repeat = 0; repeat < MAX_TEST_RETRIES; repeat++) {
612433d6423SLionel Sambuc
613433d6423SLionel Sambuc /* SCSI TEST UNIT READY OUT stage */
614433d6423SLionel Sambuc if (mass_storage_send_scsi_cbw_out(SCSI_TEST_UNIT_READY, NULL))
615*2d64210cSWojciech Zajac return EIO;
616433d6423SLionel Sambuc
617433d6423SLionel Sambuc /* TODO: Only CSW failure should normally contribute to retry */
618433d6423SLionel Sambuc
619433d6423SLionel Sambuc /* SCSI TEST UNIT READY IN stage */
620433d6423SLionel Sambuc if (EXIT_SUCCESS == mass_storage_send_scsi_csw_in())
621433d6423SLionel Sambuc return EXIT_SUCCESS;
622*2d64210cSWojciech Zajac
623*2d64210cSWojciech Zajac /* Check for errors */
624*2d64210cSWojciech Zajac if (EXIT_SUCCESS != (error = mass_storage_check_error())) {
625*2d64210cSWojciech Zajac MASS_MSG("SCSI sense error checking failed");
626*2d64210cSWojciech Zajac return error;
627433d6423SLionel Sambuc }
628433d6423SLionel Sambuc
629*2d64210cSWojciech Zajac /* Ignore potential signal interruption (no return value check),
630*2d64210cSWojciech Zajac * since it causes driver termination anyway */
631*2d64210cSWojciech Zajac if (nanosleep(&test_wait, NULL))
632*2d64210cSWojciech Zajac MASS_MSG("Calling nanosleep() failed");
633*2d64210cSWojciech Zajac }
634*2d64210cSWojciech Zajac
635*2d64210cSWojciech Zajac return EIO;
636*2d64210cSWojciech Zajac }
637*2d64210cSWojciech Zajac
638*2d64210cSWojciech Zajac
639*2d64210cSWojciech Zajac /*===========================================================================*
640*2d64210cSWojciech Zajac * mass_storage_check_error *
641*2d64210cSWojciech Zajac *===========================================================================*/
642*2d64210cSWojciech Zajac static int
mass_storage_check_error(void)643*2d64210cSWojciech Zajac mass_storage_check_error(void)
644*2d64210cSWojciech Zajac {
645*2d64210cSWojciech Zajac /* SCSI sense structure for local use */
646*2d64210cSWojciech Zajac typedef struct MASS_PACKED scsi_sense {
647*2d64210cSWojciech Zajac
648*2d64210cSWojciech Zajac uint8_t code : 7;
649*2d64210cSWojciech Zajac uint8_t valid : 1;
650*2d64210cSWojciech Zajac uint8_t obsolete : 8;
651*2d64210cSWojciech Zajac uint8_t sense : 4;
652*2d64210cSWojciech Zajac uint8_t reserved : 1;
653*2d64210cSWojciech Zajac uint8_t ili : 1;
654*2d64210cSWojciech Zajac uint8_t eom : 1;
655*2d64210cSWojciech Zajac uint8_t filemark : 1;
656*2d64210cSWojciech Zajac uint32_t information : 32;
657*2d64210cSWojciech Zajac uint8_t additional_len : 8;
658*2d64210cSWojciech Zajac uint32_t command_specific : 32;
659*2d64210cSWojciech Zajac uint8_t additional_code : 8;
660*2d64210cSWojciech Zajac uint8_t additional_qual : 8;
661*2d64210cSWojciech Zajac uint8_t unit_code : 8;
662*2d64210cSWojciech Zajac uint8_t key_specific1 : 7;
663*2d64210cSWojciech Zajac uint8_t sksv : 1;
664*2d64210cSWojciech Zajac uint16_t key_specific2 : 16;
665*2d64210cSWojciech Zajac }
666*2d64210cSWojciech Zajac scsi_sense;
667*2d64210cSWojciech Zajac
668*2d64210cSWojciech Zajac /* Sense variable to hold received data */
669*2d64210cSWojciech Zajac scsi_sense sense;
670*2d64210cSWojciech Zajac
671*2d64210cSWojciech Zajac MASS_DEBUG_DUMP;
672*2d64210cSWojciech Zajac
673*2d64210cSWojciech Zajac /* Check if bit-fields are packed correctly */
674*2d64210cSWojciech Zajac __CTASSERT(sizeof(sense) == SCSI_REQUEST_SENSE_DATA_LEN);
675*2d64210cSWojciech Zajac
676*2d64210cSWojciech Zajac /* SCSI REQUEST SENSE OUT stage */
677*2d64210cSWojciech Zajac if (mass_storage_send_scsi_cbw_out(SCSI_REQUEST_SENSE, NULL))
678*2d64210cSWojciech Zajac return EIO;
679*2d64210cSWojciech Zajac
680*2d64210cSWojciech Zajac /* SCSI REQUEST SENSE first IN stage */
681*2d64210cSWojciech Zajac if (mass_storage_send_scsi_data_in(&sense, sizeof(sense)))
682*2d64210cSWojciech Zajac return EIO;
683*2d64210cSWojciech Zajac
684*2d64210cSWojciech Zajac /* SCSI REQUEST SENSE second IN stage */
685*2d64210cSWojciech Zajac if (mass_storage_send_scsi_csw_in())
686*2d64210cSWojciech Zajac return EIO;
687*2d64210cSWojciech Zajac
688*2d64210cSWojciech Zajac /* When any sense code is present something may have failed */
689*2d64210cSWojciech Zajac if (sense.sense) {
690*2d64210cSWojciech Zajac #ifdef MASS_DEBUG
691*2d64210cSWojciech Zajac MASS_MSG("SCSI sense: ");
692*2d64210cSWojciech Zajac MASS_MSG("code : %8X", sense.code );
693*2d64210cSWojciech Zajac MASS_MSG("valid : %8X", sense.valid );
694*2d64210cSWojciech Zajac MASS_MSG("obsolete : %8X", sense.obsolete );
695*2d64210cSWojciech Zajac MASS_MSG("sense : %8X", sense.sense );
696*2d64210cSWojciech Zajac MASS_MSG("reserved : %8X", sense.reserved );
697*2d64210cSWojciech Zajac MASS_MSG("ili : %8X", sense.ili );
698*2d64210cSWojciech Zajac MASS_MSG("eom : %8X", sense.eom );
699*2d64210cSWojciech Zajac MASS_MSG("filemark : %8X", sense.filemark );
700*2d64210cSWojciech Zajac MASS_MSG("information : %8X", sense.information );
701*2d64210cSWojciech Zajac MASS_MSG("additional_len : %8X", sense.additional_len );
702*2d64210cSWojciech Zajac MASS_MSG("command_specific : %8X", sense.command_specific);
703*2d64210cSWojciech Zajac MASS_MSG("additional_code : %8X", sense.additional_code );
704*2d64210cSWojciech Zajac MASS_MSG("additional_qual : %8X", sense.additional_qual );
705*2d64210cSWojciech Zajac MASS_MSG("unit_code : %8X", sense.unit_code );
706*2d64210cSWojciech Zajac MASS_MSG("key_specific1 : %8X", sense.key_specific1 );
707*2d64210cSWojciech Zajac MASS_MSG("sksv : %8X", sense.sksv );
708*2d64210cSWojciech Zajac MASS_MSG("key_specific2 : %8X", sense.key_specific2 );
709*2d64210cSWojciech Zajac #else
710*2d64210cSWojciech Zajac MASS_MSG("SCSI sense: 0x%02X 0x%02X 0x%02X", sense.sense,
711*2d64210cSWojciech Zajac sense.additional_code, sense.additional_qual);
712*2d64210cSWojciech Zajac #endif
713*2d64210cSWojciech Zajac }
714*2d64210cSWojciech Zajac
715*2d64210cSWojciech Zajac return EXIT_SUCCESS;
716433d6423SLionel Sambuc }
717433d6423SLionel Sambuc
718433d6423SLionel Sambuc
719433d6423SLionel Sambuc /*===========================================================================*
720433d6423SLionel Sambuc * mass_storage_try_first_open *
721433d6423SLionel Sambuc *===========================================================================*/
722433d6423SLionel Sambuc static int
mass_storage_try_first_open()723433d6423SLionel Sambuc mass_storage_try_first_open()
724433d6423SLionel Sambuc {
725433d6423SLionel Sambuc unsigned int llba;
726433d6423SLionel Sambuc unsigned int blen;
727433d6423SLionel Sambuc unsigned char inquiry[SCSI_INQUIRY_DATA_LEN];
728433d6423SLionel Sambuc unsigned char capacity[SCSI_READ_CAPACITY_DATA_LEN];
729433d6423SLionel Sambuc
730433d6423SLionel Sambuc MASS_DEBUG_DUMP;
731433d6423SLionel Sambuc
732433d6423SLionel Sambuc assert(NULL != driver_state.cur_drive);
733433d6423SLionel Sambuc
734433d6423SLionel Sambuc llba = 0; /* Last logical block address */
735433d6423SLionel Sambuc blen = 0; /* Block length (usually 512B) */
736433d6423SLionel Sambuc
737433d6423SLionel Sambuc /* Run TEST UNIT READY before other SCSI command
738433d6423SLionel Sambuc * Some devices refuse to work without this */
739433d6423SLionel Sambuc if (mass_storage_test())
740433d6423SLionel Sambuc return EIO;
741433d6423SLionel Sambuc
742433d6423SLionel Sambuc /* SCSI INQUIRY OUT stage */
743433d6423SLionel Sambuc if (mass_storage_send_scsi_cbw_out(SCSI_INQUIRY, NULL))
744433d6423SLionel Sambuc return EIO;
745433d6423SLionel Sambuc
746433d6423SLionel Sambuc /* SCSI INQUIRY first IN stage */
747433d6423SLionel Sambuc if (mass_storage_send_scsi_data_in(inquiry, sizeof(inquiry)))
748433d6423SLionel Sambuc return EIO;
749433d6423SLionel Sambuc
750433d6423SLionel Sambuc /* SCSI INQUIRY second IN stage */
751433d6423SLionel Sambuc if (mass_storage_send_scsi_csw_in())
752433d6423SLionel Sambuc return EIO;
753433d6423SLionel Sambuc
754433d6423SLionel Sambuc /* Check for proper reply */
755433d6423SLionel Sambuc if (check_inquiry_reply(inquiry))
756433d6423SLionel Sambuc return EIO;
757433d6423SLionel Sambuc
758433d6423SLionel Sambuc /* Run TEST UNIT READY before other SCSI command
759433d6423SLionel Sambuc * Some devices refuse to work without this */
760433d6423SLionel Sambuc if (mass_storage_test())
761433d6423SLionel Sambuc return EIO;
762433d6423SLionel Sambuc
763433d6423SLionel Sambuc /* SCSI READ CAPACITY OUT stage */
764433d6423SLionel Sambuc if (mass_storage_send_scsi_cbw_out(SCSI_READ_CAPACITY, NULL))
765433d6423SLionel Sambuc return EIO;
766433d6423SLionel Sambuc
767433d6423SLionel Sambuc /* SCSI READ CAPACITY first IN stage */
768433d6423SLionel Sambuc if (mass_storage_send_scsi_data_in(capacity, sizeof(capacity)))
769433d6423SLionel Sambuc return EIO;
770433d6423SLionel Sambuc
771433d6423SLionel Sambuc /* SCSI READ CAPACITY second IN stage */
772433d6423SLionel Sambuc if (mass_storage_send_scsi_csw_in())
773433d6423SLionel Sambuc return EIO;
774433d6423SLionel Sambuc
775433d6423SLionel Sambuc /* Check for proper reply */
776433d6423SLionel Sambuc if (check_read_capacity_reply(capacity, &llba, &blen))
777433d6423SLionel Sambuc return EIO;
778433d6423SLionel Sambuc
779433d6423SLionel Sambuc /* For now only Minix's default SECTOR_SIZE is supported */
780433d6423SLionel Sambuc if (SECTOR_SIZE != blen)
781433d6423SLionel Sambuc panic("Invalid block size used by USB device!");
782433d6423SLionel Sambuc
783433d6423SLionel Sambuc /* Get information about capacity from reply */
784433d6423SLionel Sambuc driver_state.cur_drive->disk.dv_base = 0;
785433d6423SLionel Sambuc driver_state.cur_drive->disk.dv_size = llba * blen;
786433d6423SLionel Sambuc
787433d6423SLionel Sambuc return EXIT_SUCCESS;
788433d6423SLionel Sambuc }
789433d6423SLionel Sambuc
790433d6423SLionel Sambuc
791433d6423SLionel Sambuc /*===========================================================================*
792433d6423SLionel Sambuc * mass_storage_transfer_restrictions *
793433d6423SLionel Sambuc *===========================================================================*/
794433d6423SLionel Sambuc static int
mass_storage_transfer_restrictions(u64_t pos,unsigned long bytes)795433d6423SLionel Sambuc mass_storage_transfer_restrictions(u64_t pos, unsigned long bytes)
796433d6423SLionel Sambuc {
797433d6423SLionel Sambuc MASS_DEBUG_DUMP;
798433d6423SLionel Sambuc
799433d6423SLionel Sambuc assert(NULL != driver_state.cur_device);
800433d6423SLionel Sambuc
801433d6423SLionel Sambuc /* Zero-length request must not be issued */
802433d6423SLionel Sambuc if (0 == bytes) {
803433d6423SLionel Sambuc MASS_MSG("Transfer request length equals 0");
804433d6423SLionel Sambuc return EINVAL;
805433d6423SLionel Sambuc }
806433d6423SLionel Sambuc
807433d6423SLionel Sambuc /* Starting position must be aligned to sector */
808433d6423SLionel Sambuc if (0 != (pos % SECTOR_SIZE)) {
809433d6423SLionel Sambuc MASS_MSG("Transfer position not divisible by %u", SECTOR_SIZE);
810433d6423SLionel Sambuc return EINVAL;
811433d6423SLionel Sambuc }
812433d6423SLionel Sambuc
813433d6423SLionel Sambuc /* Length must be integer multiple of sector sizes */
814433d6423SLionel Sambuc if (0 != (bytes % SECTOR_SIZE)) {
815433d6423SLionel Sambuc MASS_MSG("Data length not divisible by %u", SECTOR_SIZE);
816433d6423SLionel Sambuc return EINVAL;
817433d6423SLionel Sambuc }
818433d6423SLionel Sambuc
819433d6423SLionel Sambuc /* Guard against ridiculous 64B overflow */
820433d6423SLionel Sambuc if ((pos + bytes) <= pos) {
821433d6423SLionel Sambuc MASS_MSG("Request outside available address space");
822433d6423SLionel Sambuc return EINVAL;
823433d6423SLionel Sambuc }
824433d6423SLionel Sambuc
825433d6423SLionel Sambuc return EXIT_SUCCESS;
826433d6423SLionel Sambuc }
827433d6423SLionel Sambuc
828433d6423SLionel Sambuc
829433d6423SLionel Sambuc /*===========================================================================*
830433d6423SLionel Sambuc * mass_storage_write *
831433d6423SLionel Sambuc *===========================================================================*/
832433d6423SLionel Sambuc static ssize_t
mass_storage_write(unsigned long sector_number,endpoint_t endpt,iovec_t * iov,unsigned int iov_count,unsigned long bytes_left)833433d6423SLionel Sambuc mass_storage_write(unsigned long sector_number,
834433d6423SLionel Sambuc endpoint_t endpt,
835433d6423SLionel Sambuc iovec_t * iov,
836433d6423SLionel Sambuc unsigned int iov_count,
837433d6423SLionel Sambuc unsigned long bytes_left)
838433d6423SLionel Sambuc {
839433d6423SLionel Sambuc /*
840433d6423SLionel Sambuc * This function writes whatever was put in 'iov' array
841433d6423SLionel Sambuc * (iov[0] : iov[iov_count]), into continuous region of mass storage,
842433d6423SLionel Sambuc * starting from sector 'sector_number'. Total amount of 'iov'
843433d6423SLionel Sambuc * data should be greater or equal to initial value of 'bytes_left'.
844433d6423SLionel Sambuc *
845433d6423SLionel Sambuc * Endpoint value 'endpt', determines if vectors 'iov' contain memory
846433d6423SLionel Sambuc * addresses for copying or grant IDs.
847433d6423SLionel Sambuc */
848433d6423SLionel Sambuc
849433d6423SLionel Sambuc iov_state cur_iov; /* Current state of vector copying */
850433d6423SLionel Sambuc unsigned long bytes_to_write; /* To be written in this iteration */
851433d6423SLionel Sambuc ssize_t bytes_already_written; /* Total amount written (retval) */
852433d6423SLionel Sambuc
853433d6423SLionel Sambuc MASS_DEBUG_DUMP;
854433d6423SLionel Sambuc
855433d6423SLionel Sambuc /* Initialize locals */
856433d6423SLionel Sambuc cur_iov.remaining_bytes = 0; /* No IO vector initially */
857433d6423SLionel Sambuc cur_iov.iov_idx = 0; /* Starting from first vector */
858433d6423SLionel Sambuc bytes_already_written = 0; /* Nothing copied yet */
859433d6423SLionel Sambuc
860433d6423SLionel Sambuc /* Mass storage operations are sector based */
861433d6423SLionel Sambuc assert(0 == (sizeof(buffer) % SECTOR_SIZE));
862433d6423SLionel Sambuc assert(0 == (bytes_left % SECTOR_SIZE));
863433d6423SLionel Sambuc
864433d6423SLionel Sambuc while (bytes_left > 0) {
865433d6423SLionel Sambuc
866433d6423SLionel Sambuc /* Fill write buffer with data from IO Vectors */
867433d6423SLionel Sambuc {
868433d6423SLionel Sambuc unsigned long buf_offset;
869433d6423SLionel Sambuc unsigned long copy_len;
870433d6423SLionel Sambuc
871433d6423SLionel Sambuc /* Start copying to the beginning of the buffer */
872433d6423SLionel Sambuc buf_offset = 0;
873433d6423SLionel Sambuc
874433d6423SLionel Sambuc /* Copy as long as not copied vectors exist or
875433d6423SLionel Sambuc * buffer is not fully filled */
876433d6423SLionel Sambuc for (;;) {
877433d6423SLionel Sambuc
878433d6423SLionel Sambuc /* If entire previous vector
879433d6423SLionel Sambuc * was used get new one */
880433d6423SLionel Sambuc if (0 == cur_iov.remaining_bytes) {
881433d6423SLionel Sambuc /* Check if there are
882433d6423SLionel Sambuc * vectors to copied */
883433d6423SLionel Sambuc if (cur_iov.iov_idx < iov_count) {
884433d6423SLionel Sambuc
885433d6423SLionel Sambuc cur_iov.base_addr =
886433d6423SLionel Sambuc iov[cur_iov.iov_idx].iov_addr;
887433d6423SLionel Sambuc cur_iov.remaining_bytes =
888433d6423SLionel Sambuc iov[cur_iov.iov_idx].iov_size;
889433d6423SLionel Sambuc cur_iov.offset = 0;
890433d6423SLionel Sambuc cur_iov.iov_idx++;
891433d6423SLionel Sambuc
892433d6423SLionel Sambuc } else {
893433d6423SLionel Sambuc /* All vectors copied */
894433d6423SLionel Sambuc break;
895433d6423SLionel Sambuc }
896433d6423SLionel Sambuc }
897433d6423SLionel Sambuc
898433d6423SLionel Sambuc /* Copy as much as it is possible from vector
899433d6423SLionel Sambuc * and at most the amount that can be
900433d6423SLionel Sambuc * put in buffer */
901433d6423SLionel Sambuc copy_len = MIN(sizeof(buffer) - buf_offset,
902433d6423SLionel Sambuc cur_iov.remaining_bytes);
903433d6423SLionel Sambuc
904433d6423SLionel Sambuc /* This distinction is required as transfer can
905433d6423SLionel Sambuc * be used from within this process and meaning
906433d6423SLionel Sambuc * of IO vector'a address is different than
907433d6423SLionel Sambuc * grant ID */
908433d6423SLionel Sambuc if (endpt == SELF) {
909433d6423SLionel Sambuc memcpy(&buffer[buf_offset],
910433d6423SLionel Sambuc (void*)(cur_iov.base_addr +
911433d6423SLionel Sambuc cur_iov.offset), copy_len);
912433d6423SLionel Sambuc } else {
913433d6423SLionel Sambuc ssize_t copy_res;
914433d6423SLionel Sambuc if ((copy_res = sys_safecopyfrom(endpt,
915433d6423SLionel Sambuc cur_iov.base_addr,
916433d6423SLionel Sambuc cur_iov.offset,
917433d6423SLionel Sambuc (vir_bytes)
918433d6423SLionel Sambuc (&buffer[buf_offset]),
919433d6423SLionel Sambuc copy_len))) {
920433d6423SLionel Sambuc MASS_MSG("sys_safecopyfrom "
921433d6423SLionel Sambuc "failed");
922433d6423SLionel Sambuc return copy_res;
923433d6423SLionel Sambuc }
924433d6423SLionel Sambuc }
925433d6423SLionel Sambuc
926433d6423SLionel Sambuc /* Alter current state of copying */
927433d6423SLionel Sambuc buf_offset += copy_len;
928433d6423SLionel Sambuc cur_iov.offset += copy_len;
929433d6423SLionel Sambuc cur_iov.remaining_bytes -= copy_len;
930433d6423SLionel Sambuc
931433d6423SLionel Sambuc /* Buffer was filled */
932433d6423SLionel Sambuc if (sizeof(buffer) == buf_offset)
933433d6423SLionel Sambuc break;
934433d6423SLionel Sambuc }
935433d6423SLionel Sambuc
936433d6423SLionel Sambuc /* Determine how many bytes from copied buffer we wish
937433d6423SLionel Sambuc * to write, buf_offset represents total amount of
938433d6423SLionel Sambuc * bytes copied above */
939433d6423SLionel Sambuc if (bytes_left >= buf_offset) {
940433d6423SLionel Sambuc bytes_to_write = buf_offset;
941433d6423SLionel Sambuc bytes_left -= buf_offset;
942433d6423SLionel Sambuc } else {
943433d6423SLionel Sambuc bytes_to_write = bytes_left;
944433d6423SLionel Sambuc bytes_left = 0;
945433d6423SLionel Sambuc }
946433d6423SLionel Sambuc }
947433d6423SLionel Sambuc
948433d6423SLionel Sambuc /* Send URB and alter sector number */
949433d6423SLionel Sambuc if (bytes_to_write > 0) {
950433d6423SLionel Sambuc
951433d6423SLionel Sambuc scsi_transfer info;
952433d6423SLionel Sambuc
953433d6423SLionel Sambuc info.length = bytes_to_write;
954433d6423SLionel Sambuc info.lba = sector_number;
955433d6423SLionel Sambuc
956433d6423SLionel Sambuc /* SCSI WRITE first OUT stage */
957433d6423SLionel Sambuc if (mass_storage_send_scsi_cbw_out(SCSI_WRITE, &info))
958433d6423SLionel Sambuc return EIO;
959433d6423SLionel Sambuc
960433d6423SLionel Sambuc /* SCSI WRITE second OUT stage */
961433d6423SLionel Sambuc if (mass_storage_send_scsi_data_out(buffer,
962433d6423SLionel Sambuc bytes_to_write))
963433d6423SLionel Sambuc return EIO;
964433d6423SLionel Sambuc
965433d6423SLionel Sambuc /* SCSI WRITE IN stage */
966433d6423SLionel Sambuc if (mass_storage_send_scsi_csw_in())
967433d6423SLionel Sambuc return EIO;
968433d6423SLionel Sambuc
969433d6423SLionel Sambuc /* Writing completed so shift starting
970433d6423SLionel Sambuc * sector for next iteration */
971433d6423SLionel Sambuc sector_number += bytes_to_write / SECTOR_SIZE;
972433d6423SLionel Sambuc
973433d6423SLionel Sambuc /* Update amount of data already copied */
974433d6423SLionel Sambuc bytes_already_written += bytes_to_write;
975433d6423SLionel Sambuc }
976433d6423SLionel Sambuc }
977433d6423SLionel Sambuc
978433d6423SLionel Sambuc return bytes_already_written;
979433d6423SLionel Sambuc }
980433d6423SLionel Sambuc
981433d6423SLionel Sambuc
982433d6423SLionel Sambuc /*===========================================================================*
983433d6423SLionel Sambuc * mass_storage_read *
984433d6423SLionel Sambuc *===========================================================================*/
985433d6423SLionel Sambuc static ssize_t
mass_storage_read(unsigned long sector_number,endpoint_t endpt,iovec_t * iov,unsigned int iov_count,unsigned long bytes_left)986433d6423SLionel Sambuc mass_storage_read(unsigned long sector_number,
987433d6423SLionel Sambuc endpoint_t endpt,
988433d6423SLionel Sambuc iovec_t * iov,
989433d6423SLionel Sambuc unsigned int iov_count,
990433d6423SLionel Sambuc unsigned long bytes_left)
991433d6423SLionel Sambuc {
992433d6423SLionel Sambuc /*
993433d6423SLionel Sambuc * This function reads 'bytes_left' bytes of mass storage data into
994433d6423SLionel Sambuc * 'iov' array (iov[0] : iov[iov_count]) starting from sector
995433d6423SLionel Sambuc * 'sector_number'. Total amount of 'iov' data should be greater or
996433d6423SLionel Sambuc * equal to initial value of 'bytes_left'.
997433d6423SLionel Sambuc *
998433d6423SLionel Sambuc * Endpoint value 'endpt', determines if vectors 'iov' contain memory
999433d6423SLionel Sambuc * addresses for copying or grant IDs.
1000433d6423SLionel Sambuc */
1001433d6423SLionel Sambuc
1002433d6423SLionel Sambuc iov_state cur_iov; /* Current state of vector copying */
1003433d6423SLionel Sambuc unsigned long bytes_to_read; /* To be read in this iteration */
1004433d6423SLionel Sambuc ssize_t bytes_already_read; /* Total amount read (retval) */
1005433d6423SLionel Sambuc
1006433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1007433d6423SLionel Sambuc
1008433d6423SLionel Sambuc /* Initialize locals */
1009433d6423SLionel Sambuc cur_iov.remaining_bytes = 0; /* No IO vector initially */
1010433d6423SLionel Sambuc cur_iov.iov_idx = 0; /* Starting from first vector */
1011433d6423SLionel Sambuc bytes_already_read = 0; /* Nothing copied yet */
1012433d6423SLionel Sambuc
1013433d6423SLionel Sambuc /* Mass storage operations are sector based */
1014433d6423SLionel Sambuc assert(0 == (sizeof(buffer) % SECTOR_SIZE));
1015433d6423SLionel Sambuc assert(0 == (bytes_left % SECTOR_SIZE));
1016433d6423SLionel Sambuc
1017433d6423SLionel Sambuc while (bytes_left > 0) {
1018433d6423SLionel Sambuc
1019433d6423SLionel Sambuc /* Decide read length and alter remaining bytes */
1020433d6423SLionel Sambuc {
1021433d6423SLionel Sambuc /* Number of bytes to be read in next URB */
1022433d6423SLionel Sambuc if (bytes_left > sizeof(buffer)) {
1023433d6423SLionel Sambuc bytes_to_read = sizeof(buffer);
1024433d6423SLionel Sambuc } else {
1025433d6423SLionel Sambuc bytes_to_read = bytes_left;
1026433d6423SLionel Sambuc }
1027433d6423SLionel Sambuc
1028433d6423SLionel Sambuc bytes_left -= bytes_to_read;
1029433d6423SLionel Sambuc }
1030433d6423SLionel Sambuc
1031433d6423SLionel Sambuc /* Send URB and alter sector number */
1032433d6423SLionel Sambuc {
1033433d6423SLionel Sambuc scsi_transfer info;
1034433d6423SLionel Sambuc
1035433d6423SLionel Sambuc info.length = bytes_to_read;
1036433d6423SLionel Sambuc info.lba = sector_number;
1037433d6423SLionel Sambuc
1038433d6423SLionel Sambuc /* SCSI READ OUT stage */
1039433d6423SLionel Sambuc if (mass_storage_send_scsi_cbw_out(SCSI_READ, &info))
1040433d6423SLionel Sambuc return EIO;
1041433d6423SLionel Sambuc
1042433d6423SLionel Sambuc /* SCSI READ first IN stage */
1043433d6423SLionel Sambuc if (mass_storage_send_scsi_data_in(buffer,
1044433d6423SLionel Sambuc bytes_to_read))
1045433d6423SLionel Sambuc return EIO;
1046433d6423SLionel Sambuc
1047433d6423SLionel Sambuc /* SCSI READ second IN stage */
1048433d6423SLionel Sambuc if (mass_storage_send_scsi_csw_in())
1049433d6423SLionel Sambuc return EIO;
1050433d6423SLionel Sambuc
1051433d6423SLionel Sambuc /* Reading completed so shift starting
1052433d6423SLionel Sambuc * sector for next iteration */
1053433d6423SLionel Sambuc sector_number += bytes_to_read / SECTOR_SIZE;
1054433d6423SLionel Sambuc }
1055433d6423SLionel Sambuc
1056433d6423SLionel Sambuc /* Fill IO Vectors with data from buffer */
1057433d6423SLionel Sambuc {
1058433d6423SLionel Sambuc unsigned long buf_offset;
1059433d6423SLionel Sambuc unsigned long copy_len;
1060433d6423SLionel Sambuc
1061433d6423SLionel Sambuc /* Start copying from the beginning of the buffer */
1062433d6423SLionel Sambuc buf_offset = 0;
1063433d6423SLionel Sambuc
1064433d6423SLionel Sambuc /* Copy as long as there are unfilled vectors
1065433d6423SLionel Sambuc * or data in buffer remains */
1066433d6423SLionel Sambuc for (;;) {
1067433d6423SLionel Sambuc
1068433d6423SLionel Sambuc /* If previous vector was filled get new one */
1069433d6423SLionel Sambuc if (0 == cur_iov.remaining_bytes) {
1070433d6423SLionel Sambuc /* Check if there are vectors
1071433d6423SLionel Sambuc * to be filled */
1072433d6423SLionel Sambuc if (cur_iov.iov_idx < iov_count) {
1073433d6423SLionel Sambuc
1074433d6423SLionel Sambuc cur_iov.base_addr =
1075433d6423SLionel Sambuc iov[cur_iov.iov_idx].iov_addr;
1076433d6423SLionel Sambuc cur_iov.remaining_bytes =
1077433d6423SLionel Sambuc iov[cur_iov.iov_idx].iov_size;
1078433d6423SLionel Sambuc cur_iov.offset = 0;
1079433d6423SLionel Sambuc cur_iov.iov_idx++;
1080433d6423SLionel Sambuc
1081433d6423SLionel Sambuc } else {
1082433d6423SLionel Sambuc /* Total length of vectors
1083433d6423SLionel Sambuc * should be greater or equal
1084433d6423SLionel Sambuc * to initial value of
1085433d6423SLionel Sambuc * bytes_left. Being here means
1086433d6423SLionel Sambuc * that everything should
1087433d6423SLionel Sambuc * have been copied already */
1088433d6423SLionel Sambuc assert(0 == bytes_left);
1089433d6423SLionel Sambuc break;
1090433d6423SLionel Sambuc }
1091433d6423SLionel Sambuc }
1092433d6423SLionel Sambuc
1093433d6423SLionel Sambuc /* Copy as much as it is possible from buffer
1094433d6423SLionel Sambuc * and at most the amount that can be
1095433d6423SLionel Sambuc * put in vector */
1096433d6423SLionel Sambuc copy_len = MIN(bytes_to_read - buf_offset,
1097433d6423SLionel Sambuc cur_iov.remaining_bytes);
1098433d6423SLionel Sambuc
1099433d6423SLionel Sambuc /* This distinction is required as transfer can
1100433d6423SLionel Sambuc * be used from within this process and meaning
1101433d6423SLionel Sambuc * of IO vector'a address is different than
1102433d6423SLionel Sambuc * grant ID */
1103433d6423SLionel Sambuc if (endpt == SELF) {
1104433d6423SLionel Sambuc memcpy((void*)(cur_iov.base_addr +
1105433d6423SLionel Sambuc cur_iov.offset),
1106433d6423SLionel Sambuc &buffer[buf_offset],
1107433d6423SLionel Sambuc copy_len);
1108433d6423SLionel Sambuc } else {
1109433d6423SLionel Sambuc ssize_t copy_res;
1110433d6423SLionel Sambuc if ((copy_res = sys_safecopyto(endpt,
1111433d6423SLionel Sambuc cur_iov.base_addr,
1112433d6423SLionel Sambuc cur_iov.offset,
1113433d6423SLionel Sambuc (vir_bytes)
1114433d6423SLionel Sambuc (&buffer[buf_offset]),
1115433d6423SLionel Sambuc copy_len))) {
1116433d6423SLionel Sambuc MASS_MSG("sys_safecopyto "
1117433d6423SLionel Sambuc "failed");
1118433d6423SLionel Sambuc return copy_res;
1119433d6423SLionel Sambuc }
1120433d6423SLionel Sambuc }
1121433d6423SLionel Sambuc
1122433d6423SLionel Sambuc /* Alter current state of copying */
1123433d6423SLionel Sambuc buf_offset += copy_len;
1124433d6423SLionel Sambuc cur_iov.offset += copy_len;
1125433d6423SLionel Sambuc cur_iov.remaining_bytes -= copy_len;
1126433d6423SLionel Sambuc
1127433d6423SLionel Sambuc /* Everything was copied */
1128433d6423SLionel Sambuc if (bytes_to_read == buf_offset)
1129433d6423SLionel Sambuc break;
1130433d6423SLionel Sambuc }
1131433d6423SLionel Sambuc
1132433d6423SLionel Sambuc /* Update amount of data already copied */
1133433d6423SLionel Sambuc bytes_already_read += buf_offset;
1134433d6423SLionel Sambuc }
1135433d6423SLionel Sambuc }
1136433d6423SLionel Sambuc
1137433d6423SLionel Sambuc return bytes_already_read;
1138433d6423SLionel Sambuc }
1139433d6423SLionel Sambuc
1140433d6423SLionel Sambuc
1141433d6423SLionel Sambuc /*===========================================================================*
1142433d6423SLionel Sambuc * mass_storage_open *
1143433d6423SLionel Sambuc *===========================================================================*/
1144433d6423SLionel Sambuc static int
mass_storage_open(devminor_t minor,int UNUSED (access))1145433d6423SLionel Sambuc mass_storage_open(devminor_t minor, int UNUSED(access))
1146433d6423SLionel Sambuc {
1147433d6423SLionel Sambuc mass_storage_drive * d;
1148433d6423SLionel Sambuc int r;
1149433d6423SLionel Sambuc
1150433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1151433d6423SLionel Sambuc
1152433d6423SLionel Sambuc /* Decode minor into drive device information */
1153433d6423SLionel Sambuc if (NULL == (mass_storage_part(minor)))
1154433d6423SLionel Sambuc return ENXIO;
1155433d6423SLionel Sambuc
1156433d6423SLionel Sambuc /* Copy evaluated current drive for simplified dereference */
1157433d6423SLionel Sambuc d = driver_state.cur_drive;
1158433d6423SLionel Sambuc
1159433d6423SLionel Sambuc #ifdef MASS_RESET_RECOVERY
1160433d6423SLionel Sambuc /* In case of previous CBW mismatch */
1161433d6423SLionel Sambuc if (mass_storage_reset_recovery()) {
1162433d6423SLionel Sambuc MASS_MSG("Resetting mass storage device failed");
1163433d6423SLionel Sambuc return EIO;
1164433d6423SLionel Sambuc }
1165433d6423SLionel Sambuc #endif
1166433d6423SLionel Sambuc
1167433d6423SLionel Sambuc /* In case of missing endpoint information, do simple
1168433d6423SLionel Sambuc * enumeration and hold it for future use */
1169433d6423SLionel Sambuc if ((URB_INVALID_EP == driver_state.cur_periph->ep_in.ep_num) ||
1170433d6423SLionel Sambuc (URB_INVALID_EP == driver_state.cur_periph->ep_out.ep_num)) {
1171433d6423SLionel Sambuc
1172433d6423SLionel Sambuc if (mass_storage_get_endpoints(&driver_state.cur_periph->ep_in,
1173433d6423SLionel Sambuc &driver_state.cur_periph->ep_out))
1174433d6423SLionel Sambuc return EIO;
1175433d6423SLionel Sambuc }
1176433d6423SLionel Sambuc
1177433d6423SLionel Sambuc /* If drive hasn't been opened yet, try to open it */
1178433d6423SLionel Sambuc if (d->open_ct == 0) {
1179433d6423SLionel Sambuc if ((r = mass_storage_try_first_open())) {
1180433d6423SLionel Sambuc MASS_MSG("Opening mass storage device"
1181433d6423SLionel Sambuc " for the first time failed");
1182*2d64210cSWojciech Zajac
1183*2d64210cSWojciech Zajac /* Do one more test before failing, to output
1184*2d64210cSWojciech Zajac * sense errors in case they weren't dumped already */
1185*2d64210cSWojciech Zajac if (mass_storage_test())
1186*2d64210cSWojciech Zajac MASS_MSG("Final TEST UNIT READY failed");
1187*2d64210cSWojciech Zajac
1188433d6423SLionel Sambuc return r;
1189433d6423SLionel Sambuc }
1190433d6423SLionel Sambuc
1191433d6423SLionel Sambuc /* Clear remembered device state for current
1192433d6423SLionel Sambuc * drive before calling partition */
1193433d6423SLionel Sambuc memset(&d->part[0], 0, sizeof(d->part));
1194433d6423SLionel Sambuc memset(&d->subpart[0], 0, sizeof(d->subpart));
1195433d6423SLionel Sambuc
1196433d6423SLionel Sambuc /* Try parsing partition table (for entire drive) */
1197433d6423SLionel Sambuc /* Warning!! This call uses mass_storage_part with own minors
1198433d6423SLionel Sambuc * and alters global driver_state.cur_device! */
1199433d6423SLionel Sambuc partition(&mass_storage, (d->drive_idx * DEV_PER_DRIVE),
1200433d6423SLionel Sambuc P_PRIMARY, 0);
1201433d6423SLionel Sambuc
1202433d6423SLionel Sambuc /* Decode minor into UPDATED drive device information */
1203433d6423SLionel Sambuc if (NULL == (mass_storage_part(minor)))
1204433d6423SLionel Sambuc return ENXIO;
1205433d6423SLionel Sambuc
1206433d6423SLionel Sambuc /* Decoded size must be positive or else
1207433d6423SLionel Sambuc * we assume device (partition) is unavailable */
1208433d6423SLionel Sambuc if (0 == driver_state.cur_device->dv_size)
1209433d6423SLionel Sambuc return ENXIO;
1210433d6423SLionel Sambuc }
1211433d6423SLionel Sambuc
1212433d6423SLionel Sambuc /* Run TEST UNIT READY before further commands
1213433d6423SLionel Sambuc * Some devices refuse to work without this */
1214433d6423SLionel Sambuc if (mass_storage_test())
1215433d6423SLionel Sambuc return EIO;
1216433d6423SLionel Sambuc
1217433d6423SLionel Sambuc /* Opening completed */
1218433d6423SLionel Sambuc d->open_ct++;
1219433d6423SLionel Sambuc
1220433d6423SLionel Sambuc return EXIT_SUCCESS;
1221433d6423SLionel Sambuc }
1222433d6423SLionel Sambuc
1223433d6423SLionel Sambuc
1224433d6423SLionel Sambuc /*===========================================================================*
1225433d6423SLionel Sambuc * mass_storage_close *
1226433d6423SLionel Sambuc *===========================================================================*/
mass_storage_close(devminor_t minor)1227433d6423SLionel Sambuc static int mass_storage_close(devminor_t minor)
1228433d6423SLionel Sambuc {
1229433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1230433d6423SLionel Sambuc
1231433d6423SLionel Sambuc /* Decode minor into drive device information */
1232433d6423SLionel Sambuc if (NULL == (mass_storage_part(minor)))
1233433d6423SLionel Sambuc return ENXIO;
1234433d6423SLionel Sambuc
1235433d6423SLionel Sambuc /* If drive hasn't been opened yet */
1236433d6423SLionel Sambuc if (driver_state.cur_drive->open_ct == 0) {
1237433d6423SLionel Sambuc MASS_MSG("Device was not opened yet");
1238433d6423SLionel Sambuc return ERESTART;
1239433d6423SLionel Sambuc }
1240433d6423SLionel Sambuc
1241433d6423SLionel Sambuc /* Act accordingly */
1242433d6423SLionel Sambuc driver_state.cur_drive->open_ct--;
1243433d6423SLionel Sambuc
1244433d6423SLionel Sambuc return EXIT_SUCCESS;
1245433d6423SLionel Sambuc }
1246433d6423SLionel Sambuc
1247433d6423SLionel Sambuc
1248433d6423SLionel Sambuc /*===========================================================================*
1249433d6423SLionel Sambuc * mass_storage_transfer *
1250433d6423SLionel Sambuc *===========================================================================*/
1251433d6423SLionel Sambuc static ssize_t
mass_storage_transfer(devminor_t minor,int do_write,u64_t pos,endpoint_t endpt,iovec_t * iov,unsigned int iov_count,int UNUSED (flags))1252433d6423SLionel Sambuc mass_storage_transfer(devminor_t minor, /* device minor number */
1253433d6423SLionel Sambuc int do_write, /* 1 write, 0 read */
1254433d6423SLionel Sambuc u64_t pos, /* position of starting point */
1255433d6423SLionel Sambuc endpoint_t endpt, /* endpoint */
1256433d6423SLionel Sambuc iovec_t * iov, /* vector */
1257433d6423SLionel Sambuc unsigned int iov_count, /* how many vectors */
1258433d6423SLionel Sambuc int UNUSED(flags)) /* transfer flags */
1259433d6423SLionel Sambuc {
1260433d6423SLionel Sambuc u64_t temp_sector_number;
1261433d6423SLionel Sambuc unsigned long bytes;
1262433d6423SLionel Sambuc unsigned long sector_number;
1263433d6423SLionel Sambuc unsigned int cur_iov_idx;
1264433d6423SLionel Sambuc int r;
1265433d6423SLionel Sambuc
1266433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1267433d6423SLionel Sambuc
1268433d6423SLionel Sambuc /* Decode minor into drive device information */
1269433d6423SLionel Sambuc if (NULL == (mass_storage_part(minor)))
1270433d6423SLionel Sambuc return ENXIO;
1271433d6423SLionel Sambuc
1272433d6423SLionel Sambuc bytes = 0;
1273433d6423SLionel Sambuc
1274433d6423SLionel Sambuc /* How much data is going to be transferred? */
1275433d6423SLionel Sambuc for (cur_iov_idx = 0; cur_iov_idx < iov_count; ++cur_iov_idx) {
1276433d6423SLionel Sambuc
1277433d6423SLionel Sambuc /* Check if grant ID was supplied
1278433d6423SLionel Sambuc * instead of address and if it is valid */
1279433d6423SLionel Sambuc if (endpt != SELF)
1280433d6423SLionel Sambuc if (!GRANT_VALID((cp_grant_id_t)
1281433d6423SLionel Sambuc (iov[cur_iov_idx].iov_addr)))
1282433d6423SLionel Sambuc return EINVAL;
1283433d6423SLionel Sambuc
1284433d6423SLionel Sambuc /* All supplied vectors must have positive length */
1285433d6423SLionel Sambuc if ((signed long)(iov[cur_iov_idx].iov_size) <= 0) {
1286433d6423SLionel Sambuc MASS_MSG("Transfer request length is not positive");
1287433d6423SLionel Sambuc return EINVAL;
1288433d6423SLionel Sambuc }
1289433d6423SLionel Sambuc
1290433d6423SLionel Sambuc /* Requirements were met, more bytes can be transferred */
1291433d6423SLionel Sambuc bytes += iov[cur_iov_idx].iov_size;
1292433d6423SLionel Sambuc
1293433d6423SLionel Sambuc /* Request size must never overflow */
1294433d6423SLionel Sambuc if ((signed long)bytes <= 0) {
1295433d6423SLionel Sambuc MASS_MSG("Transfer request length overflowed");
1296433d6423SLionel Sambuc return EINVAL;
1297433d6423SLionel Sambuc }
1298433d6423SLionel Sambuc }
1299433d6423SLionel Sambuc
1300433d6423SLionel Sambuc /* Check if reading beyond device/partition */
1301433d6423SLionel Sambuc if (pos >= driver_state.cur_device->dv_size) {
1302433d6423SLionel Sambuc MASS_MSG("Request out of bounds for given device");
1303433d6423SLionel Sambuc return 0; /* No error and no bytes read */
1304433d6423SLionel Sambuc }
1305433d6423SLionel Sambuc
1306433d6423SLionel Sambuc /* Check if arguments agree with accepted restriction
1307433d6423SLionel Sambuc * for parameters of transfer */
1308433d6423SLionel Sambuc if ((r = mass_storage_transfer_restrictions(pos, bytes)))
1309433d6423SLionel Sambuc return r;
1310433d6423SLionel Sambuc
1311433d6423SLionel Sambuc /* If 'hard' requirements above were met, do additional
1312433d6423SLionel Sambuc * limiting to device/partition boundary */
1313433d6423SLionel Sambuc if ((pos + bytes) > driver_state.cur_device->dv_size)
1314433d6423SLionel Sambuc bytes = (driver_state.cur_device->dv_size - pos) &
1315433d6423SLionel Sambuc ~SECTOR_MASK;
1316433d6423SLionel Sambuc
1317433d6423SLionel Sambuc /* We have to obtain sector number of given position
1318433d6423SLionel Sambuc * and limit it to what URB can handle */
1319433d6423SLionel Sambuc temp_sector_number = (driver_state.cur_device->dv_base + pos) /
1320433d6423SLionel Sambuc SECTOR_SIZE;
1321433d6423SLionel Sambuc assert(temp_sector_number < ULONG_MAX); /* LBA is limited to 32B */
1322433d6423SLionel Sambuc sector_number = (unsigned long)temp_sector_number;
1323433d6423SLionel Sambuc
1324433d6423SLionel Sambuc if (do_write)
1325433d6423SLionel Sambuc return mass_storage_write(sector_number, endpt, iov,
1326433d6423SLionel Sambuc iov_count, bytes);
1327433d6423SLionel Sambuc else
1328433d6423SLionel Sambuc return mass_storage_read(sector_number, endpt, iov,
1329433d6423SLionel Sambuc iov_count, bytes);
1330433d6423SLionel Sambuc }
1331433d6423SLionel Sambuc
1332433d6423SLionel Sambuc
1333433d6423SLionel Sambuc /*===========================================================================*
1334433d6423SLionel Sambuc * mass_storage_ioctl *
1335433d6423SLionel Sambuc *===========================================================================*/
1336433d6423SLionel Sambuc static int
mass_storage_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,endpoint_t UNUSED (user_endpt))1337433d6423SLionel Sambuc mass_storage_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
1338433d6423SLionel Sambuc cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
1339433d6423SLionel Sambuc {
1340433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1341433d6423SLionel Sambuc
1342433d6423SLionel Sambuc /* Decode minor into drive device information */
1343433d6423SLionel Sambuc if (NULL == (mass_storage_part(minor)))
1344433d6423SLionel Sambuc return ENXIO;
1345433d6423SLionel Sambuc
1346433d6423SLionel Sambuc switch (request) {
1347433d6423SLionel Sambuc case DIOCOPENCT:
1348433d6423SLionel Sambuc if (sys_safecopyto(endpt, grant, 0,
1349433d6423SLionel Sambuc (vir_bytes) &(driver_state.cur_drive->open_ct),
1350433d6423SLionel Sambuc sizeof(driver_state.cur_drive->open_ct)))
1351433d6423SLionel Sambuc panic("sys_safecopyto failed!");
1352433d6423SLionel Sambuc
1353433d6423SLionel Sambuc return EXIT_SUCCESS;
1354433d6423SLionel Sambuc
1355433d6423SLionel Sambuc default:
1356433d6423SLionel Sambuc MASS_MSG("Unimplemented IOCTL request: 0x%X",
1357433d6423SLionel Sambuc (int)request);
1358433d6423SLionel Sambuc break;
1359433d6423SLionel Sambuc }
1360433d6423SLionel Sambuc
1361433d6423SLionel Sambuc return ENOTTY;
1362433d6423SLionel Sambuc }
1363433d6423SLionel Sambuc
1364433d6423SLionel Sambuc
1365433d6423SLionel Sambuc /*===========================================================================*
1366433d6423SLionel Sambuc * mass_storage_cleanup *
1367433d6423SLionel Sambuc *===========================================================================*/
mass_storage_cleanup(void)1368433d6423SLionel Sambuc static void mass_storage_cleanup(void)
1369433d6423SLionel Sambuc {
1370433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1371433d6423SLionel Sambuc return;
1372433d6423SLionel Sambuc }
1373433d6423SLionel Sambuc
1374433d6423SLionel Sambuc
1375433d6423SLionel Sambuc /*===========================================================================*
1376433d6423SLionel Sambuc * mass_storage_part *
1377433d6423SLionel Sambuc *===========================================================================*/
1378433d6423SLionel Sambuc static struct device *
mass_storage_part(devminor_t minor)1379433d6423SLionel Sambuc mass_storage_part(devminor_t minor)
1380433d6423SLionel Sambuc {
1381433d6423SLionel Sambuc unsigned long sel_drive;
1382433d6423SLionel Sambuc unsigned long sel_device;
1383433d6423SLionel Sambuc
1384433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1385433d6423SLionel Sambuc
1386433d6423SLionel Sambuc /* Override every time before further decision */
1387433d6423SLionel Sambuc driver_state.cur_drive = NULL;
1388433d6423SLionel Sambuc driver_state.cur_device = NULL;
1389433d6423SLionel Sambuc driver_state.cur_periph = NULL;
1390433d6423SLionel Sambuc
1391433d6423SLionel Sambuc /* Decode 'minor' code to find which device file was used */
1392433d6423SLionel Sambuc if (minor < MINOR_d0p0s0) {
1393433d6423SLionel Sambuc
1394433d6423SLionel Sambuc /* No sub-partitions used */
1395433d6423SLionel Sambuc sel_drive = minor / DEV_PER_DRIVE;
1396433d6423SLionel Sambuc sel_device = minor % DEV_PER_DRIVE;
1397433d6423SLionel Sambuc
1398433d6423SLionel Sambuc /* Only valid minors */
1399433d6423SLionel Sambuc if (sel_drive < MAX_DRIVES) {
1400433d6423SLionel Sambuc /* Associate minor (device/partition)
1401433d6423SLionel Sambuc * with peripheral number */
1402433d6423SLionel Sambuc /* TODO:PERIPH
1403433d6423SLionel Sambuc * Proper peripheral selection based
1404433d6423SLionel Sambuc * on minor should be here: */
1405433d6423SLionel Sambuc driver_state.cur_periph = &driver_state.periph[0];
1406433d6423SLionel Sambuc
1407433d6423SLionel Sambuc /* Select drive entry for opening count etc. */
1408433d6423SLionel Sambuc driver_state.cur_drive =
1409433d6423SLionel Sambuc &(driver_state.cur_periph->drives[sel_drive]);
1410433d6423SLionel Sambuc
1411433d6423SLionel Sambuc /* Select device entry for given device file */
1412433d6423SLionel Sambuc /* Device 0 means entire drive.
1413433d6423SLionel Sambuc * Devices 1,2,3,4 mean partitions 0,1,2,3 */
1414433d6423SLionel Sambuc if (0 == sel_device)
1415433d6423SLionel Sambuc driver_state.cur_device =
1416433d6423SLionel Sambuc &(driver_state.cur_drive->disk);
1417433d6423SLionel Sambuc else
1418433d6423SLionel Sambuc driver_state.cur_device =
1419433d6423SLionel Sambuc &(driver_state.cur_drive->part
1420433d6423SLionel Sambuc [sel_device-1]);
1421433d6423SLionel Sambuc }
1422433d6423SLionel Sambuc
1423433d6423SLionel Sambuc } else {
1424433d6423SLionel Sambuc
1425433d6423SLionel Sambuc /* Shift values accordingly */
1426433d6423SLionel Sambuc minor -= MINOR_d0p0s0;
1427433d6423SLionel Sambuc
1428433d6423SLionel Sambuc /* Sub-partitions used */
1429433d6423SLionel Sambuc sel_drive = minor / SUBPART_PER_DISK;
1430433d6423SLionel Sambuc sel_device = minor % SUBPART_PER_DISK;
1431433d6423SLionel Sambuc
1432433d6423SLionel Sambuc /* Only valid minors */
1433433d6423SLionel Sambuc if (sel_drive < MAX_DRIVES) {
1434433d6423SLionel Sambuc /* Leave in case of ridiculously high number */
1435433d6423SLionel Sambuc if (minor < SUBPART_PER_DISK) {
1436433d6423SLionel Sambuc /* Associate minor (device/partition)
1437433d6423SLionel Sambuc * with peripheral number */
1438433d6423SLionel Sambuc /* TODO:PERIPH
1439433d6423SLionel Sambuc * Proper peripheral selection based
1440433d6423SLionel Sambuc * on minor should be here: */
1441433d6423SLionel Sambuc driver_state.cur_periph =
1442433d6423SLionel Sambuc &driver_state.periph[0];
1443433d6423SLionel Sambuc
1444433d6423SLionel Sambuc /* Select drive entry for opening count etc. */
1445433d6423SLionel Sambuc driver_state.cur_drive =
1446433d6423SLionel Sambuc &(driver_state.cur_periph->drives
1447433d6423SLionel Sambuc [sel_drive]);
1448433d6423SLionel Sambuc /* Select device entry for given
1449433d6423SLionel Sambuc * sub-partition device file */
1450433d6423SLionel Sambuc driver_state.cur_device =
1451433d6423SLionel Sambuc &(driver_state.cur_drive->subpart
1452433d6423SLionel Sambuc [sel_device]);
1453433d6423SLionel Sambuc }
1454433d6423SLionel Sambuc }
1455433d6423SLionel Sambuc }
1456433d6423SLionel Sambuc
1457433d6423SLionel Sambuc /* Check for success */
1458433d6423SLionel Sambuc if (NULL == driver_state.cur_device) {
1459433d6423SLionel Sambuc MASS_MSG("Device for minor: %u not found", minor);
1460433d6423SLionel Sambuc } else {
1461433d6423SLionel Sambuc /* Assign index as well */
1462433d6423SLionel Sambuc driver_state.cur_drive->drive_idx = sel_drive;
1463433d6423SLionel Sambuc }
1464433d6423SLionel Sambuc
1465433d6423SLionel Sambuc return driver_state.cur_device;
1466433d6423SLionel Sambuc }
1467433d6423SLionel Sambuc
1468433d6423SLionel Sambuc
1469433d6423SLionel Sambuc /*===========================================================================*
1470433d6423SLionel Sambuc * mass_storage_geometry *
1471433d6423SLionel Sambuc *===========================================================================*/
1472*2d64210cSWojciech Zajac /* This command is optional for most mass storage devices
1473*2d64210cSWojciech Zajac * It should rather be used with USB floppy disk reader */
1474433d6423SLionel Sambuc #ifdef MASS_USE_GEOMETRY
1475433d6423SLionel Sambuc static void
mass_storage_geometry(devminor_t minor,struct part_geom * part)1476433d6423SLionel Sambuc mass_storage_geometry(devminor_t minor, struct part_geom * part)
1477433d6423SLionel Sambuc {
1478433d6423SLionel Sambuc char flexible_disk_page[SCSI_MODE_SENSE_FLEX_DATA_LEN];
1479433d6423SLionel Sambuc
1480433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1481433d6423SLionel Sambuc
1482433d6423SLionel Sambuc /* Decode minor into drive device information */
1483433d6423SLionel Sambuc if (NULL == (mass_storage_part(minor)))
1484433d6423SLionel Sambuc return;
1485433d6423SLionel Sambuc
1486433d6423SLionel Sambuc /* SCSI MODE SENSE OUT stage */
1487433d6423SLionel Sambuc if (mass_storage_send_scsi_cbw_out(SCSI_MODE_SENSE, NULL))
1488433d6423SLionel Sambuc return;
1489433d6423SLionel Sambuc
1490433d6423SLionel Sambuc /* SCSI MODE SENSE first IN stage */
1491433d6423SLionel Sambuc if (mass_storage_send_scsi_data_in(flexible_disk_page,
1492433d6423SLionel Sambuc sizeof(flexible_disk_page)))
1493433d6423SLionel Sambuc return;
1494433d6423SLionel Sambuc
1495433d6423SLionel Sambuc /* SCSI MODE SENSE second IN stage */
1496433d6423SLionel Sambuc if (mass_storage_send_scsi_csw_in())
1497433d6423SLionel Sambuc return;
1498433d6423SLionel Sambuc
1499433d6423SLionel Sambuc /* Get geometry from reply */
1500433d6423SLionel Sambuc if (check_mode_sense_reply(flexible_disk_page, &(part->cylinders),
1501433d6423SLionel Sambuc &(part->heads), &(part->sectors)))
1502433d6423SLionel Sambuc return;
1503433d6423SLionel Sambuc #else
1504433d6423SLionel Sambuc static void
1505433d6423SLionel Sambuc mass_storage_geometry(devminor_t UNUSED(minor), struct part_geom * part)
1506433d6423SLionel Sambuc {
1507433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1508433d6423SLionel Sambuc
1509433d6423SLionel Sambuc part->cylinders = part->size / SECTOR_SIZE;
1510433d6423SLionel Sambuc part->heads = 64;
1511433d6423SLionel Sambuc part->sectors = 32;
1512433d6423SLionel Sambuc #endif
1513433d6423SLionel Sambuc }
1514433d6423SLionel Sambuc
1515433d6423SLionel Sambuc
1516433d6423SLionel Sambuc /*===========================================================================*
1517433d6423SLionel Sambuc * usb_driver_completion *
1518433d6423SLionel Sambuc *===========================================================================*/
1519433d6423SLionel Sambuc static void
1520433d6423SLionel Sambuc usb_driver_completion(void * UNUSED(priv))
1521433d6423SLionel Sambuc {
1522433d6423SLionel Sambuc /* Last request was completed so allow continuing
1523433d6423SLionel Sambuc * execution from place where semaphore was downed */
1524433d6423SLionel Sambuc ddekit_sem_up(mass_storage_sem);
1525433d6423SLionel Sambuc }
1526433d6423SLionel Sambuc
1527433d6423SLionel Sambuc
1528433d6423SLionel Sambuc /*===========================================================================*
1529433d6423SLionel Sambuc * usb_driver_connect *
1530433d6423SLionel Sambuc *===========================================================================*/
1531433d6423SLionel Sambuc static void
1532433d6423SLionel Sambuc usb_driver_connect(struct ddekit_usb_dev * dev,
1533433d6423SLionel Sambuc unsigned int interfaces)
1534433d6423SLionel Sambuc {
1535433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1536433d6423SLionel Sambuc
1537433d6423SLionel Sambuc /* TODO:PERIPH
1538433d6423SLionel Sambuc * Some sort of more complex peripheral assignment should be here */
1539433d6423SLionel Sambuc driver_state.cur_periph = &driver_state.periph[0];
1540433d6423SLionel Sambuc
1541433d6423SLionel Sambuc if (NULL != driver_state.cur_periph->dev)
1542433d6423SLionel Sambuc panic("Only one peripheral can be connected!");
1543433d6423SLionel Sambuc
1544433d6423SLionel Sambuc /* Hold host information for future use */
1545433d6423SLionel Sambuc driver_state.cur_periph->dev = dev;
1546433d6423SLionel Sambuc driver_state.cur_periph->interfaces = interfaces;
1547433d6423SLionel Sambuc driver_state.cur_periph->ep_in.ep_num = URB_INVALID_EP;
1548433d6423SLionel Sambuc driver_state.cur_periph->ep_out.ep_num = URB_INVALID_EP;
1549433d6423SLionel Sambuc }
1550433d6423SLionel Sambuc
1551433d6423SLionel Sambuc
1552433d6423SLionel Sambuc /*===========================================================================*
1553433d6423SLionel Sambuc * usb_driver_disconnect *
1554433d6423SLionel Sambuc *===========================================================================*/
1555433d6423SLionel Sambuc static void
1556433d6423SLionel Sambuc usb_driver_disconnect(struct ddekit_usb_dev * UNUSED(dev))
1557433d6423SLionel Sambuc {
1558433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1559433d6423SLionel Sambuc
1560433d6423SLionel Sambuc /* TODO:PERIPH
1561433d6423SLionel Sambuc * Some sort of peripheral discard should be here */
1562433d6423SLionel Sambuc driver_state.cur_periph = &driver_state.periph[0];
1563433d6423SLionel Sambuc
1564433d6423SLionel Sambuc assert(NULL != driver_state.cur_periph->dev);
1565433d6423SLionel Sambuc
1566433d6423SLionel Sambuc /* Clear */
1567433d6423SLionel Sambuc driver_state.cur_periph->dev = NULL;
1568433d6423SLionel Sambuc driver_state.cur_periph->interfaces = 0;
1569433d6423SLionel Sambuc driver_state.cur_periph->ep_in.ep_num = URB_INVALID_EP;
1570433d6423SLionel Sambuc driver_state.cur_periph->ep_out.ep_num = URB_INVALID_EP;
1571433d6423SLionel Sambuc }
1572433d6423SLionel Sambuc
1573433d6423SLionel Sambuc
1574433d6423SLionel Sambuc /*===========================================================================*
1575433d6423SLionel Sambuc * mass_storage_get_endpoints *
1576433d6423SLionel Sambuc *===========================================================================*/
1577433d6423SLionel Sambuc static int
1578433d6423SLionel Sambuc mass_storage_get_endpoints(urb_ep_config * ep_in, urb_ep_config * ep_out)
1579433d6423SLionel Sambuc {
1580433d6423SLionel Sambuc /* URB to be send */
1581433d6423SLionel Sambuc struct ddekit_usb_urb urb;
1582433d6423SLionel Sambuc
1583433d6423SLionel Sambuc /* Setup buffer to be attached */
1584433d6423SLionel Sambuc struct usb_ctrlrequest setup_buf;
1585433d6423SLionel Sambuc
1586433d6423SLionel Sambuc /* Control EP configuration */
1587433d6423SLionel Sambuc urb_ep_config ep_conf;
1588433d6423SLionel Sambuc
1589433d6423SLionel Sambuc /* Descriptors' buffer */
1590433d6423SLionel Sambuc unsigned char descriptors[MAX_DESCRIPTORS_LEN];
1591433d6423SLionel Sambuc
1592433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1593433d6423SLionel Sambuc
1594433d6423SLionel Sambuc /* Initialize EP configuration */
1595433d6423SLionel Sambuc ep_conf.ep_num = 0;
1596433d6423SLionel Sambuc ep_conf.direction = DDEKIT_USB_IN;
1597433d6423SLionel Sambuc ep_conf.type = DDEKIT_USB_TRANSFER_CTL;
1598433d6423SLionel Sambuc ep_conf.max_packet_size = 0;
1599433d6423SLionel Sambuc ep_conf.interval = 0;
1600433d6423SLionel Sambuc
1601433d6423SLionel Sambuc /* Reset URB and assign given values */
1602433d6423SLionel Sambuc init_urb(&urb, driver_state.cur_periph->dev, &ep_conf);
1603433d6423SLionel Sambuc
1604433d6423SLionel Sambuc /* Clear setup data */
1605433d6423SLionel Sambuc memset(&setup_buf, 0, sizeof(setup_buf));
1606433d6423SLionel Sambuc
1607433d6423SLionel Sambuc /* Standard get endpoint request */
1608433d6423SLionel Sambuc setup_buf.bRequestType = 0x80; /* Device to host */
1609433d6423SLionel Sambuc setup_buf.bRequest = UR_GET_DESCRIPTOR;
1610433d6423SLionel Sambuc setup_buf.wValue = UDESC_CONFIG << 8; /* TODO: configuration 0 */
1611433d6423SLionel Sambuc setup_buf.wIndex = 0x00;
1612433d6423SLionel Sambuc setup_buf.wLength = MAX_DESCRIPTORS_LEN;
1613433d6423SLionel Sambuc
1614433d6423SLionel Sambuc /* Attach buffers to URB */
1615433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_SETUP,
1616433d6423SLionel Sambuc &setup_buf, sizeof(setup_buf));
1617433d6423SLionel Sambuc attach_urb_data(&urb, URB_BUF_TYPE_DATA,
1618433d6423SLionel Sambuc descriptors, sizeof(descriptors));
1619433d6423SLionel Sambuc
1620433d6423SLionel Sambuc /* Send and wait for response */
1621433d6423SLionel Sambuc if (blocking_urb_submit(&urb, mass_storage_sem,
1622433d6423SLionel Sambuc URB_SUBMIT_ALLOW_MISMATCH))
1623433d6423SLionel Sambuc return EXIT_FAILURE;
1624433d6423SLionel Sambuc
1625433d6423SLionel Sambuc /* Check if buffer was supposed to hold more data */
1626433d6423SLionel Sambuc if (urb.size < urb.actual_length) {
1627433d6423SLionel Sambuc MASS_MSG("Too much descriptor data received");
1628433d6423SLionel Sambuc return EXIT_FAILURE;
1629433d6423SLionel Sambuc }
1630433d6423SLionel Sambuc
1631433d6423SLionel Sambuc return mass_storage_parse_descriptors(urb.data, urb.actual_length,
1632433d6423SLionel Sambuc ep_in, ep_out);
1633433d6423SLionel Sambuc }
1634433d6423SLionel Sambuc
1635433d6423SLionel Sambuc
1636433d6423SLionel Sambuc /*===========================================================================*
1637433d6423SLionel Sambuc * mass_storage_parse_endpoint *
1638433d6423SLionel Sambuc *===========================================================================*/
1639433d6423SLionel Sambuc static int
1640433d6423SLionel Sambuc mass_storage_parse_endpoint(usb_descriptor_t * cur_desc,
1641433d6423SLionel Sambuc urb_ep_config * ep_in, urb_ep_config * ep_out)
1642433d6423SLionel Sambuc {
1643433d6423SLionel Sambuc usb_endpoint_descriptor_t * ep_desc;
1644433d6423SLionel Sambuc
1645433d6423SLionel Sambuc urb_ep_config * this_ep;
1646433d6423SLionel Sambuc
1647433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1648433d6423SLionel Sambuc
1649433d6423SLionel Sambuc ep_desc = (usb_endpoint_descriptor_t *)cur_desc;
1650433d6423SLionel Sambuc
1651433d6423SLionel Sambuc /* Only bulk with no other attributes are important */
1652433d6423SLionel Sambuc if (UE_BULK == ep_desc->bmAttributes) {
1653433d6423SLionel Sambuc
1654433d6423SLionel Sambuc /* Check for direction */
1655433d6423SLionel Sambuc if (UE_DIR_IN == UE_GET_DIR(ep_desc->bEndpointAddress)) {
1656433d6423SLionel Sambuc
1657433d6423SLionel Sambuc this_ep = ep_in;
1658433d6423SLionel Sambuc this_ep->direction = DDEKIT_USB_IN;
1659433d6423SLionel Sambuc
1660433d6423SLionel Sambuc } else {
1661433d6423SLionel Sambuc
1662433d6423SLionel Sambuc this_ep = ep_out;
1663433d6423SLionel Sambuc this_ep->direction = DDEKIT_USB_OUT;
1664433d6423SLionel Sambuc }
1665433d6423SLionel Sambuc
1666433d6423SLionel Sambuc /* Check if it was set before */
1667433d6423SLionel Sambuc if (URB_INVALID_EP != this_ep->ep_num) {
1668433d6423SLionel Sambuc MASS_MSG("BULK EP already set");
1669433d6423SLionel Sambuc return EXIT_FAILURE;
1670433d6423SLionel Sambuc }
1671433d6423SLionel Sambuc
1672433d6423SLionel Sambuc /* Assign rest */
1673433d6423SLionel Sambuc this_ep->ep_num = UE_GET_ADDR(ep_desc->bEndpointAddress);
1674433d6423SLionel Sambuc this_ep->type = DDEKIT_USB_TRANSFER_BLK;
1675433d6423SLionel Sambuc this_ep->max_packet_size = UGETW(ep_desc->wMaxPacketSize);
1676433d6423SLionel Sambuc this_ep->interval = ep_desc->bInterval;
1677433d6423SLionel Sambuc }
1678433d6423SLionel Sambuc
1679433d6423SLionel Sambuc /* EP type other than bulk, is also correct,
1680433d6423SLionel Sambuc * just no parsing is performed */
1681433d6423SLionel Sambuc return EXIT_SUCCESS;
1682433d6423SLionel Sambuc }
1683433d6423SLionel Sambuc
1684433d6423SLionel Sambuc /*===========================================================================*
1685433d6423SLionel Sambuc * mass_storage_parse_descriptors *
1686433d6423SLionel Sambuc *===========================================================================*/
1687433d6423SLionel Sambuc static int
1688433d6423SLionel Sambuc mass_storage_parse_descriptors(char * desc_buf, unsigned int buf_len,
1689433d6423SLionel Sambuc urb_ep_config * ep_in, urb_ep_config * ep_out)
1690433d6423SLionel Sambuc {
1691433d6423SLionel Sambuc /* Currently parsed, descriptors */
1692433d6423SLionel Sambuc usb_descriptor_t * cur_desc;
1693433d6423SLionel Sambuc usb_interface_descriptor_t * ifc_desc;
1694433d6423SLionel Sambuc
1695433d6423SLionel Sambuc /* Byte counter for descriptor parsing */
1696433d6423SLionel Sambuc unsigned int cur_byte;
1697433d6423SLionel Sambuc
1698433d6423SLionel Sambuc /* Non zero if recently parsed interface is valid for this device */
1699433d6423SLionel Sambuc int valid_interface;
1700433d6423SLionel Sambuc
1701433d6423SLionel Sambuc MASS_DEBUG_DUMP;
1702433d6423SLionel Sambuc
1703433d6423SLionel Sambuc /* Parse descriptors to get endpoints */
1704433d6423SLionel Sambuc ep_in->ep_num = URB_INVALID_EP;
1705433d6423SLionel Sambuc ep_out->ep_num = URB_INVALID_EP;
1706433d6423SLionel Sambuc valid_interface = 0;
1707433d6423SLionel Sambuc cur_byte = 0;
1708433d6423SLionel Sambuc
1709433d6423SLionel Sambuc while (cur_byte < buf_len) {
1710433d6423SLionel Sambuc
1711433d6423SLionel Sambuc /* Map descriptor to buffer */
1712433d6423SLionel Sambuc /* Structure is packed so alignment should not matter */
1713433d6423SLionel Sambuc cur_desc = (usb_descriptor_t *)&(desc_buf[cur_byte]);
1714433d6423SLionel Sambuc
1715433d6423SLionel Sambuc /* Check this so we won't be reading
1716433d6423SLionel Sambuc * memory outside the buffer */
1717433d6423SLionel Sambuc if ((cur_desc->bLength > 3) &&
1718433d6423SLionel Sambuc (cur_byte + cur_desc->bLength <= buf_len)) {
1719433d6423SLionel Sambuc
1720433d6423SLionel Sambuc /* Parse based on descriptor type */
1721433d6423SLionel Sambuc switch (cur_desc->bDescriptorType) {
1722433d6423SLionel Sambuc
1723433d6423SLionel Sambuc case UDESC_CONFIG: {
1724433d6423SLionel Sambuc if (USB_CONFIG_DESCRIPTOR_SIZE !=
1725433d6423SLionel Sambuc cur_desc->bLength) {
1726433d6423SLionel Sambuc MASS_MSG("Wrong configuration"
1727433d6423SLionel Sambuc " descriptor length");
1728433d6423SLionel Sambuc return EXIT_FAILURE;
1729433d6423SLionel Sambuc }
1730433d6423SLionel Sambuc break;
1731433d6423SLionel Sambuc }
1732433d6423SLionel Sambuc
1733433d6423SLionel Sambuc case UDESC_STRING:
1734433d6423SLionel Sambuc break;
1735433d6423SLionel Sambuc
1736433d6423SLionel Sambuc case UDESC_INTERFACE: {
1737433d6423SLionel Sambuc ifc_desc =
1738433d6423SLionel Sambuc (usb_interface_descriptor_t *)cur_desc;
1739433d6423SLionel Sambuc
1740433d6423SLionel Sambuc if (USB_INTERFACE_DESCRIPTOR_SIZE !=
1741433d6423SLionel Sambuc cur_desc->bLength) {
1742433d6423SLionel Sambuc MASS_MSG("Wrong interface"
1743433d6423SLionel Sambuc " descriptor length");
1744433d6423SLionel Sambuc return EXIT_FAILURE;
1745433d6423SLionel Sambuc }
1746433d6423SLionel Sambuc
1747433d6423SLionel Sambuc /* Check if following data is meant
1748433d6423SLionel Sambuc * for our interfaces */
1749433d6423SLionel Sambuc if ((1 << ifc_desc->bInterfaceNumber) &
1750433d6423SLionel Sambuc driver_state.cur_periph->interfaces)
1751433d6423SLionel Sambuc valid_interface = 1;
1752433d6423SLionel Sambuc else
1753433d6423SLionel Sambuc valid_interface = 0;
1754433d6423SLionel Sambuc
1755433d6423SLionel Sambuc break;
1756433d6423SLionel Sambuc }
1757433d6423SLionel Sambuc
1758433d6423SLionel Sambuc case UDESC_ENDPOINT: {
1759433d6423SLionel Sambuc if (USB_ENDPOINT_DESCRIPTOR_SIZE !=
1760433d6423SLionel Sambuc cur_desc->bLength) {
1761433d6423SLionel Sambuc MASS_MSG("Wrong endpoint"
1762433d6423SLionel Sambuc " descriptor length");
1763433d6423SLionel Sambuc return EXIT_FAILURE;
1764433d6423SLionel Sambuc }
1765433d6423SLionel Sambuc
1766433d6423SLionel Sambuc /* Previous interface was,
1767433d6423SLionel Sambuc * what we were looking for */
1768433d6423SLionel Sambuc if (valid_interface) {
1769433d6423SLionel Sambuc if (EXIT_SUCCESS !=
1770433d6423SLionel Sambuc mass_storage_parse_endpoint(
1771433d6423SLionel Sambuc cur_desc, ep_in, ep_out))
1772433d6423SLionel Sambuc return EXIT_FAILURE;
1773433d6423SLionel Sambuc
1774433d6423SLionel Sambuc }
1775433d6423SLionel Sambuc
1776433d6423SLionel Sambuc break;
1777433d6423SLionel Sambuc }
1778433d6423SLionel Sambuc
1779433d6423SLionel Sambuc default: {
1780433d6423SLionel Sambuc MASS_MSG("Wrong descriptor type");
1781433d6423SLionel Sambuc return EXIT_FAILURE;
1782433d6423SLionel Sambuc }
1783433d6423SLionel Sambuc }
1784433d6423SLionel Sambuc
1785433d6423SLionel Sambuc } else {
1786433d6423SLionel Sambuc MASS_MSG("Invalid descriptor length");
1787433d6423SLionel Sambuc return EXIT_FAILURE;
1788433d6423SLionel Sambuc }
1789433d6423SLionel Sambuc
1790433d6423SLionel Sambuc /* Get next descriptor */
1791433d6423SLionel Sambuc cur_byte += cur_desc->bLength;
1792433d6423SLionel Sambuc }
1793433d6423SLionel Sambuc
1794433d6423SLionel Sambuc /* Total length should match sum of all descriptors' lengths... */
1795433d6423SLionel Sambuc if (cur_byte > buf_len)
1796433d6423SLionel Sambuc return EXIT_FAILURE;
1797433d6423SLionel Sambuc
1798433d6423SLionel Sambuc /* ...and descriptors should be valid */
1799433d6423SLionel Sambuc if ((URB_INVALID_EP == ep_in->ep_num) ||
1800433d6423SLionel Sambuc (URB_INVALID_EP == ep_out->ep_num)) {
1801433d6423SLionel Sambuc MASS_MSG("Valid bulk endpoints not found");
1802433d6423SLionel Sambuc return EXIT_FAILURE;
1803433d6423SLionel Sambuc }
1804433d6423SLionel Sambuc
1805433d6423SLionel Sambuc return EXIT_SUCCESS;
1806433d6423SLionel Sambuc }
1807