1 | /* $NetBSD: utoppy.c,v 1.27 2016/08/20 19:44:46 jdolecek Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Steve C. Woodford. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: utoppy.c,v 1.27 2016/08/20 19:44:46 jdolecek Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/systm.h> |
37 | #include <sys/proc.h> |
38 | #include <sys/kernel.h> |
39 | #include <sys/fcntl.h> |
40 | #include <sys/device.h> |
41 | #include <sys/ioctl.h> |
42 | #include <sys/uio.h> |
43 | #include <sys/conf.h> |
44 | #include <sys/vnode.h> |
45 | #include <sys/bus.h> |
46 | |
47 | #include <lib/libkern/crc16.h> |
48 | |
49 | #include <dev/usb/usb.h> |
50 | #include <dev/usb/usbdi.h> |
51 | #include <dev/usb/usbdivar.h> |
52 | #include <dev/usb/usbdi_util.h> |
53 | #include <dev/usb/usbdevs.h> |
54 | #include <dev/usb/usb_quirks.h> |
55 | #include <dev/usb/utoppy.h> |
56 | |
57 | #undef UTOPPY_DEBUG |
58 | #ifdef UTOPPY_DEBUG |
59 | #define UTOPPY_DBG_OPEN 0x0001 |
60 | #define UTOPPY_DBG_CLOSE 0x0002 |
61 | #define UTOPPY_DBG_READ 0x0004 |
62 | #define UTOPPY_DBG_WRITE 0x0008 |
63 | #define UTOPPY_DBG_IOCTL 0x0010 |
64 | #define UTOPPY_DBG_SEND_PACKET 0x0020 |
65 | #define UTOPPY_DBG_RECV_PACKET 0x0040 |
66 | #define UTOPPY_DBG_ADDPATH 0x0080 |
67 | #define UTOPPY_DBG_READDIR 0x0100 |
68 | #define UTOPPY_DBG_DUMP 0x0200 |
69 | #define DPRINTF(l, m) \ |
70 | do { \ |
71 | if (utoppy_debug & l) \ |
72 | printf m; \ |
73 | } while (/*CONSTCOND*/0) |
74 | static int utoppy_debug = 0; |
75 | static void utoppy_dump_packet(const void *, size_t); |
76 | #define DDUMP_PACKET(p, l) \ |
77 | do { \ |
78 | if (utoppy_debug & UTOPPY_DBG_DUMP) \ |
79 | utoppy_dump_packet((p), (l)); \ |
80 | } while (/*CONSTCOND*/0) |
81 | #else |
82 | #define DPRINTF(l, m) /* nothing */ |
83 | #define DDUMP_PACKET(p, l) /* nothing */ |
84 | #endif |
85 | |
86 | |
87 | #define UTOPPY_CONFIG_NO 1 |
88 | #define UTOPPY_NUMENDPOINTS 2 |
89 | |
90 | #define UTOPPY_BSIZE 0xffff |
91 | #define UTOPPY_FRAG_SIZE 0x1000 |
92 | #define 8 |
93 | #define UTOPPY_SHORT_TIMEOUT (500) /* 0.5 seconds */ |
94 | #define UTOPPY_LONG_TIMEOUT (10 * 1000) /* 10 seconds */ |
95 | |
96 | /* Protocol Commands and Responses */ |
97 | #define UTOPPY_RESP_ERROR 0x0001 |
98 | #define UTOPPY_CMD_ACK 0x0002 |
99 | #define UTOPPY_RESP_SUCCESS UTOPPY_CMD_ACK |
100 | #define UTOPPY_CMD_CANCEL 0x0003 |
101 | #define UTOPPY_CMD_READY 0x0100 |
102 | #define UTOPPY_CMD_RESET 0x0101 |
103 | #define UTOPPY_CMD_TURBO 0x0102 |
104 | #define UTOPPY_CMD_STATS 0x1000 |
105 | #define UTOPPY_RESP_STATS_DATA 0x1001 |
106 | #define UTOPPY_CMD_READDIR 0x1002 |
107 | #define UTOPPY_RESP_READDIR_DATA 0x1003 |
108 | #define UTOPPY_RESP_READDIR_END 0x1004 |
109 | #define UTOPPY_CMD_DELETE 0x1005 |
110 | #define UTOPPY_CMD_RENAME 0x1006 |
111 | #define UTOPPY_CMD_MKDIR 0x1007 |
112 | #define UTOPPY_CMD_FILE 0x1008 |
113 | #define UTOPPY_FILE_WRITE 0 |
114 | #define UTOPPY_FILE_READ 1 |
115 | #define 0x1009 |
116 | #define UTOPPY_RESP_FILE_DATA 0x100a |
117 | #define UTOPPY_RESP_FILE_END 0x100b |
118 | |
119 | enum utoppy_state { |
120 | UTOPPY_STATE_CLOSED, |
121 | UTOPPY_STATE_OPENING, |
122 | UTOPPY_STATE_IDLE, |
123 | UTOPPY_STATE_READDIR, |
124 | UTOPPY_STATE_READFILE, |
125 | UTOPPY_STATE_WRITEFILE |
126 | }; |
127 | |
128 | struct utoppy_softc { |
129 | device_t sc_dev; |
130 | struct usbd_device *sc_udev; /* device */ |
131 | struct usbd_interface *sc_iface; /* interface */ |
132 | int sc_dying; |
133 | int sc_refcnt; |
134 | |
135 | enum utoppy_state sc_state; |
136 | u_int sc_turbo_mode; |
137 | |
138 | int sc_out; |
139 | struct usbd_pipe *sc_out_pipe; /* bulk out pipe */ |
140 | struct usbd_xfer *sc_out_xfer; |
141 | void *sc_out_buf; |
142 | void *sc_out_data; |
143 | uint64_t sc_wr_offset; |
144 | uint64_t sc_wr_size; |
145 | |
146 | int sc_in; |
147 | struct usbd_pipe *sc_in_pipe; /* bulk in pipe */ |
148 | struct usbd_xfer *sc_in_xfer; |
149 | void *sc_in_buf; |
150 | void *sc_in_data; |
151 | size_t sc_in_len; |
152 | u_int sc_in_offset; |
153 | }; |
154 | |
155 | struct { |
156 | uint16_t ; |
157 | uint16_t ; |
158 | uint16_t ; |
159 | uint16_t ; |
160 | uint8_t [0]; |
161 | }; |
162 | #define UTOPPY_OUT_INIT(sc) \ |
163 | do { \ |
164 | struct utoppy_header *_h = sc->sc_out_data; \ |
165 | _h->h_len = 0; \ |
166 | } while (/*CONSTCOND*/0) |
167 | |
168 | #define UTOPPY_MJD_1970 40587u /* MJD value for Jan 1 00:00:00 1970 */ |
169 | |
170 | #define UTOPPY_FTYPE_DIR 1 |
171 | #define UTOPPY_FTYPE_FILE 2 |
172 | |
173 | #define UTOPPY_IN_DATA(sc) \ |
174 | ((void*)&(((uint8_t*)(sc)->sc_in_data)[(sc)->sc_in_offset+UTOPPY_HEADER_SIZE])) |
175 | |
176 | dev_type_open(utoppyopen); |
177 | dev_type_close(utoppyclose); |
178 | dev_type_read(utoppyread); |
179 | dev_type_write(utoppywrite); |
180 | dev_type_ioctl(utoppyioctl); |
181 | |
182 | const struct cdevsw utoppy_cdevsw = { |
183 | .d_open = utoppyopen, |
184 | .d_close = utoppyclose, |
185 | .d_read = utoppyread, |
186 | .d_write = utoppywrite, |
187 | .d_ioctl = utoppyioctl, |
188 | .d_stop = nostop, |
189 | .d_tty = notty, |
190 | .d_poll = nopoll, |
191 | .d_mmap = nommap, |
192 | .d_kqfilter = nokqfilter, |
193 | .d_discard = nodiscard, |
194 | .d_flag = D_OTHER |
195 | }; |
196 | |
197 | #define UTOPPYUNIT(n) (minor(n)) |
198 | |
199 | int utoppy_match(device_t, cfdata_t, void *); |
200 | void utoppy_attach(device_t, device_t, void *); |
201 | int utoppy_detach(device_t, int); |
202 | int utoppy_activate(device_t, enum devact); |
203 | extern struct cfdriver utoppy_cd; |
204 | CFATTACH_DECL_NEW(utoppy, sizeof(struct utoppy_softc), utoppy_match, |
205 | utoppy_attach, utoppy_detach, utoppy_activate); |
206 | |
207 | int |
208 | utoppy_match(device_t parent, cfdata_t match, void *aux) |
209 | { |
210 | struct usb_attach_arg *uaa = aux; |
211 | |
212 | if (uaa->uaa_vendor == USB_VENDOR_TOPFIELD && |
213 | uaa->uaa_product == USB_PRODUCT_TOPFIELD_TF5000PVR) |
214 | return UMATCH_VENDOR_PRODUCT; |
215 | |
216 | return UMATCH_NONE; |
217 | } |
218 | |
219 | void |
220 | utoppy_attach(device_t parent, device_t self, void *aux) |
221 | { |
222 | struct utoppy_softc *sc = device_private(self); |
223 | struct usb_attach_arg *uaa = aux; |
224 | struct usbd_device *dev = uaa->uaa_device; |
225 | struct usbd_interface *iface; |
226 | usb_endpoint_descriptor_t *ed; |
227 | char *devinfop; |
228 | uint8_t epcount; |
229 | int i; |
230 | |
231 | sc->sc_dev = self; |
232 | |
233 | aprint_naive("\n" ); |
234 | aprint_normal("\n" ); |
235 | |
236 | devinfop = usbd_devinfo_alloc(dev, 0); |
237 | aprint_normal_dev(self, "%s\n" , devinfop); |
238 | usbd_devinfo_free(devinfop); |
239 | |
240 | sc->sc_dying = 0; |
241 | sc->sc_refcnt = 0; |
242 | sc->sc_udev = dev; |
243 | |
244 | if (usbd_set_config_index(dev, 0, 1) |
245 | || usbd_device2interface_handle(dev, 0, &iface)) { |
246 | aprint_error_dev(self, "Configuration failed\n" ); |
247 | return; |
248 | } |
249 | |
250 | epcount = 0; |
251 | (void) usbd_endpoint_count(iface, &epcount); |
252 | if (epcount != UTOPPY_NUMENDPOINTS) { |
253 | aprint_error_dev(self, "Expected %d endpoints, got %d\n" , |
254 | UTOPPY_NUMENDPOINTS, epcount); |
255 | return; |
256 | } |
257 | |
258 | sc->sc_in = -1; |
259 | sc->sc_out = -1; |
260 | |
261 | for (i = 0; i < epcount; i++) { |
262 | ed = usbd_interface2endpoint_descriptor(iface, i); |
263 | if (ed == NULL) { |
264 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
265 | return; |
266 | } |
267 | |
268 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
269 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
270 | sc->sc_in = ed->bEndpointAddress; |
271 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
272 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
273 | sc->sc_out = ed->bEndpointAddress; |
274 | } |
275 | } |
276 | |
277 | if (sc->sc_out == -1 || sc->sc_in == -1) { |
278 | aprint_error_dev(self, |
279 | "could not find bulk in/out endpoints\n" ); |
280 | sc->sc_dying = 1; |
281 | return; |
282 | } |
283 | |
284 | sc->sc_iface = iface; |
285 | sc->sc_udev = dev; |
286 | |
287 | sc->sc_out_pipe = NULL; |
288 | sc->sc_in_pipe = NULL; |
289 | |
290 | if (usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe)) { |
291 | DPRINTF(UTOPPY_DBG_OPEN, ("%s: usbd_open_pipe(OUT) failed\n" , |
292 | device_xname(sc->sc_dev))); |
293 | aprint_error_dev(self, "could not open OUT pipe\n" ); |
294 | sc->sc_dying = 1; |
295 | return; |
296 | } |
297 | |
298 | if (usbd_open_pipe(sc->sc_iface, sc->sc_in, 0, &sc->sc_in_pipe)) { |
299 | DPRINTF(UTOPPY_DBG_OPEN, ("%s: usbd_open_pipe(IN) failed\n" , |
300 | device_xname(sc->sc_dev))); |
301 | aprint_error_dev(self, "could not open IN pipe\n" ); |
302 | |
303 | usbd_close_pipe(sc->sc_out_pipe); |
304 | sc->sc_out_pipe = NULL; |
305 | sc->sc_dying = 1; |
306 | return; |
307 | } |
308 | |
309 | int error; |
310 | error = usbd_create_xfer(sc->sc_out_pipe, UTOPPY_FRAG_SIZE, 0, 0, |
311 | &sc->sc_out_xfer); |
312 | if (error) { |
313 | aprint_error_dev(self, "could not allocate bulk out xfer\n" ); |
314 | goto fail0; |
315 | } |
316 | |
317 | error = usbd_create_xfer(sc->sc_in_pipe, UTOPPY_FRAG_SIZE, |
318 | USBD_SHORT_XFER_OK, 0, &sc->sc_in_xfer); |
319 | if (error) { |
320 | aprint_error_dev(self, "could not allocate bulk in xfer\n" ); |
321 | goto fail1; |
322 | } |
323 | |
324 | sc->sc_out_buf = usbd_get_buffer(sc->sc_out_xfer); |
325 | sc->sc_in_buf = usbd_get_buffer(sc->sc_in_xfer); |
326 | |
327 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); |
328 | |
329 | return; |
330 | |
331 | fail1: usbd_destroy_xfer(sc->sc_out_xfer); |
332 | sc->sc_out_xfer = NULL; |
333 | |
334 | fail0: sc->sc_dying = 1; |
335 | return; |
336 | } |
337 | |
338 | int |
339 | utoppy_activate(device_t self, enum devact act) |
340 | { |
341 | struct utoppy_softc *sc = device_private(self); |
342 | |
343 | switch (act) { |
344 | case DVACT_DEACTIVATE: |
345 | sc->sc_dying = 1; |
346 | return 0; |
347 | default: |
348 | return EOPNOTSUPP; |
349 | } |
350 | } |
351 | |
352 | int |
353 | utoppy_detach(device_t self, int flags) |
354 | { |
355 | struct utoppy_softc *sc = device_private(self); |
356 | int maj, mn; |
357 | int s; |
358 | |
359 | sc->sc_dying = 1; |
360 | if (sc->sc_out_pipe != NULL) |
361 | usbd_abort_pipe(sc->sc_out_pipe); |
362 | if (sc->sc_in_pipe != NULL) |
363 | usbd_abort_pipe(sc->sc_in_pipe); |
364 | |
365 | if (sc->sc_in_xfer != NULL) |
366 | usbd_destroy_xfer(sc->sc_in_xfer); |
367 | if (sc->sc_out_xfer != NULL) |
368 | usbd_destroy_xfer(sc->sc_out_xfer); |
369 | |
370 | if (sc->sc_out_pipe != NULL) |
371 | usbd_close_pipe(sc->sc_out_pipe); |
372 | if (sc->sc_in_pipe != NULL) |
373 | usbd_close_pipe(sc->sc_in_pipe); |
374 | |
375 | s = splusb(); |
376 | if (--sc->sc_refcnt >= 0) |
377 | usb_detach_waitold(sc->sc_dev); |
378 | splx(s); |
379 | |
380 | /* locate the major number */ |
381 | maj = cdevsw_lookup_major(&utoppy_cdevsw); |
382 | |
383 | /* Nuke the vnodes for any open instances (calls close). */ |
384 | mn = device_unit(self); |
385 | vdevgone(maj, mn, mn, VCHR); |
386 | |
387 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
388 | |
389 | return 0; |
390 | } |
391 | |
392 | #define UTOPPY_CRC16(ccrc,b) crc16_byte((ccrc), (b)) /* from crc16.h */ |
393 | |
394 | static const int utoppy_usbdstatus_lookup[] = { |
395 | 0, /* USBD_NORMAL_COMPLETION */ |
396 | EINPROGRESS, /* USBD_IN_PROGRESS */ |
397 | EALREADY, /* USBD_PENDING_REQUESTS */ |
398 | EAGAIN, /* USBD_NOT_STARTED */ |
399 | EINVAL, /* USBD_INVAL */ |
400 | ENOMEM, /* USBD_NOMEM */ |
401 | ECONNRESET, /* USBD_CANCELLED */ |
402 | EFAULT, /* USBD_BAD_ADDRESS */ |
403 | EBUSY, /* USBD_IN_USE */ |
404 | EADDRNOTAVAIL, /* USBD_NO_ADDR */ |
405 | ENETDOWN, /* USBD_SET_ADDR_FAILED */ |
406 | EIO, /* USBD_NO_POWER */ |
407 | EMLINK, /* USBD_TOO_DEEP */ |
408 | EIO, /* USBD_IOERROR */ |
409 | ENXIO, /* USBD_NOT_CONFIGURED */ |
410 | ETIMEDOUT, /* USBD_TIMEOUT */ |
411 | EBADMSG, /* USBD_SHORT_XFER */ |
412 | EHOSTDOWN, /* USBD_STALLED */ |
413 | EINTR /* USBD_INTERRUPTED */ |
414 | }; |
415 | |
416 | static __inline int |
417 | utoppy_usbd_status2errno(usbd_status err) |
418 | { |
419 | |
420 | if (err >= USBD_ERROR_MAX) |
421 | return EFAULT; |
422 | return utoppy_usbdstatus_lookup[err]; |
423 | } |
424 | |
425 | #ifdef UTOPPY_DEBUG |
426 | static const char * |
427 | utoppy_state_string(enum utoppy_state state) |
428 | { |
429 | const char *str; |
430 | |
431 | switch (state) { |
432 | case UTOPPY_STATE_CLOSED: |
433 | str = "CLOSED" ; |
434 | break; |
435 | case UTOPPY_STATE_OPENING: |
436 | str = "OPENING" ; |
437 | break; |
438 | case UTOPPY_STATE_IDLE: |
439 | str = "IDLE" ; |
440 | break; |
441 | case UTOPPY_STATE_READDIR: |
442 | str = "READ DIRECTORY" ; |
443 | break; |
444 | case UTOPPY_STATE_READFILE: |
445 | str = "READ FILE" ; |
446 | break; |
447 | case UTOPPY_STATE_WRITEFILE: |
448 | str = "WRITE FILE" ; |
449 | break; |
450 | default: |
451 | str = "INVALID!" ; |
452 | break; |
453 | } |
454 | |
455 | return str; |
456 | } |
457 | |
458 | static void |
459 | utoppy_dump_packet(const void *b, size_t len) |
460 | { |
461 | const uint8_t *buf = b, *l; |
462 | uint8_t c; |
463 | size_t i, j; |
464 | |
465 | if (len == 0) |
466 | return; |
467 | |
468 | len = min(len, 256); |
469 | |
470 | printf("00: " ); |
471 | |
472 | for (i = 0, l = buf; i < len; i++) { |
473 | printf("%02x " , *buf++); |
474 | |
475 | if ((i % 16) == 15) { |
476 | for (j = 0; j < 16; j++) { |
477 | c = *l++; |
478 | if (c < ' ' || c > 0x7e) |
479 | c = '.'; |
480 | printf("%c" , c); |
481 | } |
482 | |
483 | printf("\n" ); |
484 | l = buf; |
485 | |
486 | if ((i + 1) < len) |
487 | printf("%02x: " , (u_int)i + 1); |
488 | } |
489 | } |
490 | |
491 | while ((i++ % 16) != 0) |
492 | printf(" " ); |
493 | |
494 | if (l < buf) { |
495 | while (l < buf) { |
496 | c = *l++; |
497 | if (c < ' ' || c > 0x7e) |
498 | c = '.'; |
499 | printf("%c" , c); |
500 | } |
501 | |
502 | printf("\n" ); |
503 | } |
504 | } |
505 | #endif |
506 | |
507 | static usbd_status |
508 | utoppy_bulk_transfer(struct usbd_xfer *xfer, struct usbd_pipe *pipe, |
509 | uint16_t flags, uint32_t timeout, void *buf, uint32_t *size) |
510 | { |
511 | usbd_status err; |
512 | |
513 | usbd_setup_xfer(xfer, 0, buf, *size, flags, timeout, NULL); |
514 | |
515 | err = usbd_sync_transfer_sig(xfer); |
516 | |
517 | usbd_get_xfer_status(xfer, NULL, NULL, size, NULL); |
518 | return err; |
519 | } |
520 | |
521 | static int |
522 | utoppy_send_packet(struct utoppy_softc *sc, uint16_t cmd, uint32_t timeout) |
523 | { |
524 | struct utoppy_header *h; |
525 | usbd_status err; |
526 | uint32_t len; |
527 | uint16_t dlen, crc; |
528 | uint8_t *data, *e, t1, t2; |
529 | |
530 | h = sc->sc_out_data; |
531 | |
532 | DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: cmd 0x%04x, " |
533 | "len %d\n" , device_xname(sc->sc_dev), (u_int)cmd, h->h_len)); |
534 | |
535 | dlen = h->h_len; |
536 | len = dlen + UTOPPY_HEADER_SIZE; |
537 | |
538 | if (len & 1) |
539 | len++; |
540 | if ((len % 64) == 0) |
541 | len += 2; |
542 | |
543 | if (len >= UTOPPY_BSIZE) { |
544 | DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: " |
545 | "packet too big (%d)\n" , device_xname(sc->sc_dev), |
546 | (int)len)); |
547 | return EINVAL; |
548 | } |
549 | |
550 | h->h_len = htole16(dlen + UTOPPY_HEADER_SIZE); |
551 | h->h_cmd2 = 0; |
552 | h->h_cmd = htole16(cmd); |
553 | |
554 | /* The command word is part of the CRC */ |
555 | crc = UTOPPY_CRC16(0, 0); |
556 | crc = UTOPPY_CRC16(crc, 0); |
557 | crc = UTOPPY_CRC16(crc, cmd >> 8); |
558 | crc = UTOPPY_CRC16(crc, cmd); |
559 | |
560 | /* |
561 | * If there is data following the header, calculate the CRC and |
562 | * byte-swap as we go. |
563 | */ |
564 | if (dlen) { |
565 | data = h->h_data; |
566 | e = data + (dlen & ~1); |
567 | |
568 | do { |
569 | t1 = data[0]; |
570 | t2 = data[1]; |
571 | crc = UTOPPY_CRC16(crc, t1); |
572 | crc = UTOPPY_CRC16(crc, t2); |
573 | *data++ = t2; |
574 | *data++ = t1; |
575 | } while (data < e); |
576 | |
577 | if (dlen & 1) { |
578 | t1 = data[0]; |
579 | crc = UTOPPY_CRC16(crc, t1); |
580 | data[1] = t1; |
581 | } |
582 | } |
583 | |
584 | h->h_crc = htole16(crc); |
585 | data = sc->sc_out_data; |
586 | |
587 | DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: total len " |
588 | "%d...\n" , device_xname(sc->sc_dev), (int)len)); |
589 | DDUMP_PACKET(data, len); |
590 | |
591 | do { |
592 | uint32_t thislen; |
593 | |
594 | thislen = min(len, UTOPPY_FRAG_SIZE); |
595 | |
596 | memcpy(sc->sc_out_buf, data, thislen); |
597 | |
598 | err = utoppy_bulk_transfer(sc->sc_out_xfer, sc->sc_out_pipe, |
599 | 0, timeout, sc->sc_out_buf, &thislen); |
600 | |
601 | if (thislen != min(len, UTOPPY_FRAG_SIZE)) { |
602 | DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: " |
603 | "utoppy_send_packet: sent %ld, err %d\n" , |
604 | device_xname(sc->sc_dev), (u_long)thislen, err)); |
605 | } |
606 | |
607 | if (err == 0) { |
608 | len -= thislen; |
609 | data += thislen; |
610 | } |
611 | } while (err == 0 && len); |
612 | |
613 | DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: " |
614 | "usbd_bulk_transfer() returned %d.\n" , |
615 | device_xname(sc->sc_dev),err)); |
616 | |
617 | return err ? utoppy_usbd_status2errno(err) : 0; |
618 | } |
619 | |
620 | static int |
621 | utoppy_recv_packet(struct utoppy_softc *sc, uint16_t *respp, uint32_t timeout) |
622 | { |
623 | struct utoppy_header *h; |
624 | usbd_status err; |
625 | uint32_t len, thislen, requested, bytesleft; |
626 | uint16_t crc; |
627 | uint8_t *data, *e, t1, t2; |
628 | |
629 | data = sc->sc_in_data; |
630 | len = 0; |
631 | bytesleft = UTOPPY_BSIZE; |
632 | |
633 | DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: ...\n" , |
634 | device_xname(sc->sc_dev))); |
635 | |
636 | do { |
637 | requested = thislen = min(bytesleft, UTOPPY_FRAG_SIZE); |
638 | |
639 | err = utoppy_bulk_transfer(sc->sc_in_xfer, sc->sc_in_pipe, |
640 | USBD_SHORT_XFER_OK, timeout, sc->sc_in_buf, |
641 | &thislen); |
642 | |
643 | DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: " |
644 | "usbd_bulk_transfer() returned %d, thislen %d, data %p\n" , |
645 | device_xname(sc->sc_dev), err, (u_int)thislen, data)); |
646 | |
647 | if (err == 0) { |
648 | memcpy(data, sc->sc_in_buf, thislen); |
649 | DDUMP_PACKET(data, thislen); |
650 | len += thislen; |
651 | bytesleft -= thislen; |
652 | data += thislen; |
653 | } |
654 | } while (err == 0 && bytesleft && thislen == requested); |
655 | |
656 | if (err) |
657 | return utoppy_usbd_status2errno(err); |
658 | |
659 | h = sc->sc_in_data; |
660 | |
661 | DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: received %d " |
662 | "bytes in total to %p\n" , device_xname(sc->sc_dev), (u_int)len, h)); |
663 | DDUMP_PACKET(h, len); |
664 | |
665 | if (len < UTOPPY_HEADER_SIZE || len < (uint32_t)le16toh(h->h_len)) { |
666 | DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: bad " |
667 | " length (len %d, h_len %d)\n" , device_xname(sc->sc_dev), |
668 | (int)len, le16toh(h->h_len))); |
669 | return EIO; |
670 | } |
671 | |
672 | len = h->h_len = le16toh(h->h_len); |
673 | h->h_crc = le16toh(h->h_crc); |
674 | *respp = h->h_cmd = le16toh(h->h_cmd); |
675 | h->h_cmd2 = le16toh(h->h_cmd2); |
676 | |
677 | /* |
678 | * To maximise data throughput when transferring files, acknowledge |
679 | * data blocks as soon as we receive them. If we detect an error |
680 | * later on, we can always cancel. |
681 | */ |
682 | if (*respp == UTOPPY_RESP_FILE_DATA) { |
683 | DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: " |
684 | "ACKing file data\n" , device_xname(sc->sc_dev))); |
685 | |
686 | UTOPPY_OUT_INIT(sc); |
687 | err = utoppy_send_packet(sc, UTOPPY_CMD_ACK, |
688 | UTOPPY_SHORT_TIMEOUT); |
689 | if (err) { |
690 | DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: " |
691 | "utoppy_recv_packet: failed to ACK file data: %d\n" , |
692 | device_xname(sc->sc_dev), err)); |
693 | return err; |
694 | } |
695 | } |
696 | |
697 | /* The command word is part of the CRC */ |
698 | crc = UTOPPY_CRC16(0, h->h_cmd2 >> 8); |
699 | crc = UTOPPY_CRC16(crc, h->h_cmd2); |
700 | crc = UTOPPY_CRC16(crc, h->h_cmd >> 8); |
701 | crc = UTOPPY_CRC16(crc, h->h_cmd); |
702 | |
703 | /* |
704 | * Extract any payload, byte-swapping and calculating the CRC16 |
705 | * as we go. |
706 | */ |
707 | if (len > UTOPPY_HEADER_SIZE) { |
708 | data = h->h_data; |
709 | e = data + ((len & ~1) - UTOPPY_HEADER_SIZE); |
710 | |
711 | while (data < e) { |
712 | t1 = data[0]; |
713 | t2 = data[1]; |
714 | crc = UTOPPY_CRC16(crc, t2); |
715 | crc = UTOPPY_CRC16(crc, t1); |
716 | *data++ = t2; |
717 | *data++ = t1; |
718 | } |
719 | |
720 | if (len & 1) { |
721 | t1 = data[1]; |
722 | crc = UTOPPY_CRC16(crc, t1); |
723 | *data = t1; |
724 | } |
725 | } |
726 | |
727 | sc->sc_in_len = (size_t) len - UTOPPY_HEADER_SIZE; |
728 | sc->sc_in_offset = 0; |
729 | |
730 | DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: len %d, " |
731 | "crc 0x%04x, hdrcrc 0x%04x\n" , device_xname(sc->sc_dev), |
732 | (int)len, crc, h->h_crc)); |
733 | DDUMP_PACKET(h, len); |
734 | |
735 | return (crc == h->h_crc) ? 0 : EBADMSG; |
736 | } |
737 | |
738 | static __inline void * |
739 | utoppy_current_ptr(void *b) |
740 | { |
741 | struct utoppy_header *h = b; |
742 | |
743 | return &h->h_data[h->h_len]; |
744 | } |
745 | |
746 | static __inline void |
747 | utoppy_advance_ptr(void *b, size_t len) |
748 | { |
749 | struct utoppy_header *h = b; |
750 | |
751 | h->h_len += len; |
752 | } |
753 | |
754 | static __inline void |
755 | utoppy_add_8(struct utoppy_softc *sc, uint8_t v) |
756 | { |
757 | struct utoppy_header *h = sc->sc_out_data; |
758 | uint8_t *p; |
759 | |
760 | p = utoppy_current_ptr(h); |
761 | *p = v; |
762 | utoppy_advance_ptr(h, sizeof(v)); |
763 | } |
764 | |
765 | static __inline void |
766 | utoppy_add_16(struct utoppy_softc *sc, uint16_t v) |
767 | { |
768 | struct utoppy_header *h = sc->sc_out_data; |
769 | uint8_t *p; |
770 | |
771 | p = utoppy_current_ptr(h); |
772 | *p++ = (uint8_t)(v >> 8); |
773 | *p = (uint8_t)v; |
774 | utoppy_advance_ptr(h, sizeof(v)); |
775 | } |
776 | |
777 | static __inline void |
778 | utoppy_add_32(struct utoppy_softc *sc, uint32_t v) |
779 | { |
780 | struct utoppy_header *h = sc->sc_out_data; |
781 | uint8_t *p; |
782 | |
783 | p = utoppy_current_ptr(h); |
784 | *p++ = (uint8_t)(v >> 24); |
785 | *p++ = (uint8_t)(v >> 16); |
786 | *p++ = (uint8_t)(v >> 8); |
787 | *p = (uint8_t)v; |
788 | utoppy_advance_ptr(h, sizeof(v)); |
789 | } |
790 | |
791 | static __inline void |
792 | utoppy_add_64(struct utoppy_softc *sc, uint64_t v) |
793 | { |
794 | struct utoppy_header *h = sc->sc_out_data; |
795 | uint8_t *p; |
796 | |
797 | p = utoppy_current_ptr(h); |
798 | *p++ = (uint8_t)(v >> 56); |
799 | *p++ = (uint8_t)(v >> 48); |
800 | *p++ = (uint8_t)(v >> 40); |
801 | *p++ = (uint8_t)(v >> 32); |
802 | *p++ = (uint8_t)(v >> 24); |
803 | *p++ = (uint8_t)(v >> 16); |
804 | *p++ = (uint8_t)(v >> 8); |
805 | *p = (uint8_t)v; |
806 | utoppy_advance_ptr(h, sizeof(v)); |
807 | } |
808 | |
809 | static __inline void |
810 | utoppy_add_string(struct utoppy_softc *sc, const char *str, size_t len) |
811 | { |
812 | struct utoppy_header *h = sc->sc_out_data; |
813 | char *p; |
814 | |
815 | p = utoppy_current_ptr(h); |
816 | memset(p, 0, len); |
817 | strncpy(p, str, len); |
818 | utoppy_advance_ptr(h, len); |
819 | } |
820 | |
821 | static int |
822 | utoppy_add_path(struct utoppy_softc *sc, const char *path, int putlen) |
823 | { |
824 | struct utoppy_header *h = sc->sc_out_data; |
825 | uint8_t *p, *str, *s; |
826 | size_t len; |
827 | int err; |
828 | |
829 | p = utoppy_current_ptr(h); |
830 | |
831 | str = putlen ? (p + sizeof(uint16_t)) : p; |
832 | |
833 | err = copyinstr(path, str, UTOPPY_MAX_FILENAME_LEN, &len); |
834 | |
835 | DPRINTF(UTOPPY_DBG_ADDPATH, ("utoppy_add_path: err %d, len %d\n" , |
836 | err, (int)len)); |
837 | |
838 | if (err) |
839 | return err; |
840 | |
841 | if (len < 2) |
842 | return EINVAL; |
843 | |
844 | /* |
845 | * copyinstr(9) has already copied the terminating NUL character, |
846 | * but we append another one in case we have to pad the length |
847 | * later on. |
848 | */ |
849 | str[len] = '\0'; |
850 | |
851 | /* |
852 | * The Toppy uses backslash as the directory separator, so convert |
853 | * all forward slashes. |
854 | */ |
855 | for (s = &str[len - 2]; s >= str; s--) |
856 | if (*s == '/') |
857 | *s = '\\'; |
858 | |
859 | if ((len + h->h_len) & 1) |
860 | len++; |
861 | |
862 | if (putlen) |
863 | utoppy_add_16(sc, len); |
864 | |
865 | utoppy_advance_ptr(h, len); |
866 | |
867 | DPRINTF(UTOPPY_DBG_ADDPATH, ("utoppy_add_path: final len %d\n" , |
868 | (u_int)len)); |
869 | |
870 | return 0; |
871 | } |
872 | |
873 | static __inline int |
874 | utoppy_get_8(struct utoppy_softc *sc, uint8_t *vp) |
875 | { |
876 | uint8_t *p; |
877 | |
878 | if (sc->sc_in_len < sizeof(*vp)) |
879 | return 1; |
880 | |
881 | p = UTOPPY_IN_DATA(sc); |
882 | *vp = *p; |
883 | sc->sc_in_offset += sizeof(*vp); |
884 | sc->sc_in_len -= sizeof(*vp); |
885 | return 0; |
886 | } |
887 | |
888 | static __inline int |
889 | utoppy_get_16(struct utoppy_softc *sc, uint16_t *vp) |
890 | { |
891 | uint16_t v; |
892 | uint8_t *p; |
893 | |
894 | if (sc->sc_in_len < sizeof(v)) |
895 | return 1; |
896 | |
897 | p = UTOPPY_IN_DATA(sc); |
898 | v = *p++; |
899 | v = (v << 8) | *p; |
900 | *vp = v; |
901 | sc->sc_in_offset += sizeof(v); |
902 | sc->sc_in_len -= sizeof(v); |
903 | return 0; |
904 | } |
905 | |
906 | static __inline int |
907 | utoppy_get_32(struct utoppy_softc *sc, uint32_t *vp) |
908 | { |
909 | uint32_t v; |
910 | uint8_t *p; |
911 | |
912 | if (sc->sc_in_len < sizeof(v)) |
913 | return 1; |
914 | |
915 | p = UTOPPY_IN_DATA(sc); |
916 | v = *p++; |
917 | v = (v << 8) | *p++; |
918 | v = (v << 8) | *p++; |
919 | v = (v << 8) | *p; |
920 | *vp = v; |
921 | sc->sc_in_offset += sizeof(v); |
922 | sc->sc_in_len -= sizeof(v); |
923 | return 0; |
924 | } |
925 | |
926 | static __inline int |
927 | utoppy_get_64(struct utoppy_softc *sc, uint64_t *vp) |
928 | { |
929 | uint64_t v; |
930 | uint8_t *p; |
931 | |
932 | if (sc->sc_in_len < sizeof(v)) |
933 | return 1; |
934 | |
935 | p = UTOPPY_IN_DATA(sc); |
936 | v = *p++; |
937 | v = (v << 8) | *p++; |
938 | v = (v << 8) | *p++; |
939 | v = (v << 8) | *p++; |
940 | v = (v << 8) | *p++; |
941 | v = (v << 8) | *p++; |
942 | v = (v << 8) | *p++; |
943 | v = (v << 8) | *p; |
944 | *vp = v; |
945 | sc->sc_in_offset += sizeof(v); |
946 | sc->sc_in_len -= sizeof(v); |
947 | return 0; |
948 | } |
949 | |
950 | static __inline int |
951 | utoppy_get_string(struct utoppy_softc *sc, char *str, size_t len) |
952 | { |
953 | char *p; |
954 | |
955 | if (sc->sc_in_len < len) |
956 | return 1; |
957 | |
958 | memset(str, 0, len); |
959 | p = UTOPPY_IN_DATA(sc); |
960 | strncpy(str, p, len); |
961 | sc->sc_in_offset += len; |
962 | sc->sc_in_len -= len; |
963 | return 0; |
964 | } |
965 | |
966 | static int |
967 | utoppy_command(struct utoppy_softc *sc, uint16_t cmd, int timeout, |
968 | uint16_t *presp) |
969 | { |
970 | int err; |
971 | |
972 | err = utoppy_send_packet(sc, cmd, timeout); |
973 | if (err) |
974 | return err; |
975 | |
976 | err = utoppy_recv_packet(sc, presp, timeout); |
977 | if (err == EBADMSG) { |
978 | UTOPPY_OUT_INIT(sc); |
979 | utoppy_send_packet(sc, UTOPPY_RESP_ERROR, timeout); |
980 | } |
981 | |
982 | return err; |
983 | } |
984 | |
985 | static int |
986 | utoppy_timestamp_decode(struct utoppy_softc *sc, time_t *tp) |
987 | { |
988 | uint16_t mjd; |
989 | uint8_t hour, minute, sec; |
990 | uint32_t rv; |
991 | |
992 | if (utoppy_get_16(sc, &mjd) || utoppy_get_8(sc, &hour) || |
993 | utoppy_get_8(sc, &minute) || utoppy_get_8(sc, &sec)) |
994 | return 1; |
995 | |
996 | if (mjd == 0xffffu && hour == 0xffu && minute == 0xffu && sec == 0xffu){ |
997 | *tp = 0; |
998 | return 0; |
999 | } |
1000 | |
1001 | rv = (mjd < UTOPPY_MJD_1970) ? UTOPPY_MJD_1970 : (uint32_t) mjd; |
1002 | |
1003 | /* Calculate seconds since 1970 */ |
1004 | rv = (rv - UTOPPY_MJD_1970) * 60 * 60 * 24; |
1005 | |
1006 | /* Add in the hours, minutes, and seconds */ |
1007 | rv += (uint32_t)hour * 60 * 60; |
1008 | rv += (uint32_t)minute * 60; |
1009 | rv += sec; |
1010 | *tp = (time_t)rv; |
1011 | |
1012 | return 0; |
1013 | } |
1014 | |
1015 | static void |
1016 | utoppy_timestamp_encode(struct utoppy_softc *sc, time_t t) |
1017 | { |
1018 | u_int mjd, hour, minute; |
1019 | |
1020 | mjd = t / (60 * 60 * 24); |
1021 | t -= mjd * 60 * 60 * 24; |
1022 | |
1023 | hour = t / (60 * 60); |
1024 | t -= hour * 60 * 60; |
1025 | |
1026 | minute = t / 60; |
1027 | t -= minute * 60; |
1028 | |
1029 | utoppy_add_16(sc, mjd + UTOPPY_MJD_1970); |
1030 | utoppy_add_8(sc, hour); |
1031 | utoppy_add_8(sc, minute); |
1032 | utoppy_add_8(sc, t); |
1033 | } |
1034 | |
1035 | static int |
1036 | utoppy_turbo_mode(struct utoppy_softc *sc, int state) |
1037 | { |
1038 | uint16_t r; |
1039 | int err; |
1040 | |
1041 | UTOPPY_OUT_INIT(sc); |
1042 | utoppy_add_32(sc, state); |
1043 | |
1044 | err = utoppy_command(sc, UTOPPY_CMD_TURBO, UTOPPY_SHORT_TIMEOUT, &r); |
1045 | if (err) |
1046 | return err; |
1047 | |
1048 | return (r == UTOPPY_RESP_SUCCESS) ? 0 : EIO; |
1049 | } |
1050 | |
1051 | static int |
1052 | utoppy_check_ready(struct utoppy_softc *sc) |
1053 | { |
1054 | uint16_t r; |
1055 | int err; |
1056 | |
1057 | UTOPPY_OUT_INIT(sc); |
1058 | |
1059 | err = utoppy_command(sc, UTOPPY_CMD_READY, UTOPPY_LONG_TIMEOUT, &r); |
1060 | if (err) |
1061 | return err; |
1062 | |
1063 | return (r == UTOPPY_RESP_SUCCESS) ? 0 : EIO; |
1064 | } |
1065 | |
1066 | static int |
1067 | utoppy_cancel(struct utoppy_softc *sc) |
1068 | { |
1069 | uint16_t r; |
1070 | int err, i; |
1071 | |
1072 | /* |
1073 | * Issue the cancel command serveral times. the Toppy doesn't |
1074 | * always respond to the first. |
1075 | */ |
1076 | for (i = 0; i < 3; i++) { |
1077 | UTOPPY_OUT_INIT(sc); |
1078 | err = utoppy_command(sc, UTOPPY_CMD_CANCEL, |
1079 | UTOPPY_SHORT_TIMEOUT, &r); |
1080 | if (err == 0 && r == UTOPPY_RESP_SUCCESS) |
1081 | break; |
1082 | err = ETIMEDOUT; |
1083 | } |
1084 | |
1085 | if (err) |
1086 | return err; |
1087 | |
1088 | /* |
1089 | * Make sure turbo mode is off, otherwise the Toppy will not |
1090 | * respond to remote control input. |
1091 | */ |
1092 | (void) utoppy_turbo_mode(sc, 0); |
1093 | |
1094 | sc->sc_state = UTOPPY_STATE_IDLE; |
1095 | return 0; |
1096 | } |
1097 | |
1098 | static int |
1099 | utoppy_stats(struct utoppy_softc *sc, struct utoppy_stats *us) |
1100 | { |
1101 | uint32_t hsize, hfree; |
1102 | uint16_t r; |
1103 | int err; |
1104 | |
1105 | UTOPPY_OUT_INIT(sc); |
1106 | err = utoppy_command(sc, UTOPPY_CMD_STATS, UTOPPY_LONG_TIMEOUT, &r); |
1107 | if (err) |
1108 | return err; |
1109 | |
1110 | if (r != UTOPPY_RESP_STATS_DATA) |
1111 | return EIO; |
1112 | |
1113 | if (utoppy_get_32(sc, &hsize) || utoppy_get_32(sc, &hfree)) |
1114 | return EIO; |
1115 | |
1116 | us->us_hdd_size = hsize; |
1117 | us->us_hdd_size *= 1024; |
1118 | us->us_hdd_free = hfree; |
1119 | us->us_hdd_free *= 1024; |
1120 | |
1121 | return 0; |
1122 | } |
1123 | |
1124 | static int |
1125 | utoppy_readdir_next(struct utoppy_softc *sc) |
1126 | { |
1127 | uint16_t resp; |
1128 | int err; |
1129 | |
1130 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: running...\n" , |
1131 | device_xname(sc->sc_dev))); |
1132 | |
1133 | /* |
1134 | * Fetch the next READDIR response |
1135 | */ |
1136 | err = utoppy_recv_packet(sc, &resp, UTOPPY_LONG_TIMEOUT); |
1137 | if (err) { |
1138 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: " |
1139 | "utoppy_recv_packet() returned %d\n" , |
1140 | device_xname(sc->sc_dev), err)); |
1141 | if (err == EBADMSG) { |
1142 | UTOPPY_OUT_INIT(sc); |
1143 | utoppy_send_packet(sc, UTOPPY_RESP_ERROR, |
1144 | UTOPPY_LONG_TIMEOUT); |
1145 | } |
1146 | utoppy_cancel(sc); |
1147 | return err; |
1148 | } |
1149 | |
1150 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: " |
1151 | "utoppy_recv_packet() returned %d, len %ld\n" , |
1152 | device_xname(sc->sc_dev), err, (u_long)sc->sc_in_len)); |
1153 | |
1154 | switch (resp) { |
1155 | case UTOPPY_RESP_READDIR_DATA: |
1156 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: " |
1157 | "UTOPPY_RESP_READDIR_DATA\n" , device_xname(sc->sc_dev))); |
1158 | |
1159 | UTOPPY_OUT_INIT(sc); |
1160 | err = utoppy_send_packet(sc, UTOPPY_CMD_ACK, |
1161 | UTOPPY_LONG_TIMEOUT); |
1162 | if (err) { |
1163 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: " |
1164 | "utoppy_send_packet(ACK) returned %d\n" , |
1165 | device_xname(sc->sc_dev), err)); |
1166 | utoppy_cancel(sc); |
1167 | return err; |
1168 | } |
1169 | sc->sc_state = UTOPPY_STATE_READDIR; |
1170 | sc->sc_in_offset = 0; |
1171 | break; |
1172 | |
1173 | case UTOPPY_RESP_READDIR_END: |
1174 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: " |
1175 | "UTOPPY_RESP_READDIR_END\n" , device_xname(sc->sc_dev))); |
1176 | |
1177 | UTOPPY_OUT_INIT(sc); |
1178 | utoppy_send_packet(sc, UTOPPY_CMD_ACK, UTOPPY_SHORT_TIMEOUT); |
1179 | sc->sc_state = UTOPPY_STATE_IDLE; |
1180 | sc->sc_in_len = 0; |
1181 | break; |
1182 | |
1183 | default: |
1184 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: " |
1185 | "bad response: 0x%x\n" , device_xname(sc->sc_dev), resp)); |
1186 | sc->sc_state = UTOPPY_STATE_IDLE; |
1187 | sc->sc_in_len = 0; |
1188 | return EIO; |
1189 | } |
1190 | |
1191 | return 0; |
1192 | } |
1193 | |
1194 | static size_t |
1195 | utoppy_readdir_decode(struct utoppy_softc *sc, struct utoppy_dirent *ud) |
1196 | { |
1197 | uint8_t ftype; |
1198 | |
1199 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_decode: bytes left" |
1200 | " %d\n" , device_xname(sc->sc_dev), (int)sc->sc_in_len)); |
1201 | |
1202 | if (utoppy_timestamp_decode(sc, &ud->ud_mtime) || |
1203 | utoppy_get_8(sc, &ftype) || utoppy_get_64(sc, &ud->ud_size) || |
1204 | utoppy_get_string(sc, ud->ud_path, UTOPPY_MAX_FILENAME_LEN + 1) || |
1205 | utoppy_get_32(sc, &ud->ud_attributes)) { |
1206 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_decode: no " |
1207 | "more to decode\n" , device_xname(sc->sc_dev))); |
1208 | return 0; |
1209 | } |
1210 | |
1211 | switch (ftype) { |
1212 | case UTOPPY_FTYPE_DIR: |
1213 | ud->ud_type = UTOPPY_DIRENT_DIRECTORY; |
1214 | break; |
1215 | case UTOPPY_FTYPE_FILE: |
1216 | ud->ud_type = UTOPPY_DIRENT_FILE; |
1217 | break; |
1218 | default: |
1219 | ud->ud_type = UTOPPY_DIRENT_UNKNOWN; |
1220 | break; |
1221 | } |
1222 | |
1223 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_decode: %s '%s', " |
1224 | "size %lld, time 0x%08lx, attr 0x%08x\n" , device_xname(sc->sc_dev), |
1225 | (ftype == UTOPPY_FTYPE_DIR) ? "DIR" : |
1226 | ((ftype == UTOPPY_FTYPE_FILE) ? "FILE" : "UNKNOWN" ), ud->ud_path, |
1227 | ud->ud_size, (u_long)ud->ud_mtime, ud->ud_attributes)); |
1228 | |
1229 | return 1; |
1230 | } |
1231 | |
1232 | static int |
1233 | utoppy_readfile_next(struct utoppy_softc *sc) |
1234 | { |
1235 | uint64_t off; |
1236 | uint16_t resp; |
1237 | int err; |
1238 | |
1239 | err = utoppy_recv_packet(sc, &resp, UTOPPY_LONG_TIMEOUT); |
1240 | if (err) { |
1241 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: " |
1242 | "utoppy_recv_packet() returned %d\n" , |
1243 | device_xname(sc->sc_dev), err)); |
1244 | utoppy_cancel(sc); |
1245 | return err; |
1246 | } |
1247 | |
1248 | switch (resp) { |
1249 | case UTOPPY_RESP_FILE_HEADER: |
1250 | /* ACK it */ |
1251 | UTOPPY_OUT_INIT(sc); |
1252 | err = utoppy_send_packet(sc, UTOPPY_CMD_ACK, |
1253 | UTOPPY_LONG_TIMEOUT); |
1254 | if (err) { |
1255 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: " |
1256 | "utoppy_send_packet(UTOPPY_CMD_ACK) returned %d\n" , |
1257 | device_xname(sc->sc_dev), err)); |
1258 | utoppy_cancel(sc); |
1259 | return err; |
1260 | } |
1261 | |
1262 | sc->sc_in_len = 0; |
1263 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: " |
1264 | "FILE_HEADER done\n" , device_xname(sc->sc_dev))); |
1265 | break; |
1266 | |
1267 | case UTOPPY_RESP_FILE_DATA: |
1268 | /* Already ACK'd */ |
1269 | if (utoppy_get_64(sc, &off)) { |
1270 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: " |
1271 | "UTOPPY_RESP_FILE_DATA did not provide offset\n" , |
1272 | device_xname(sc->sc_dev))); |
1273 | utoppy_cancel(sc); |
1274 | return EBADMSG; |
1275 | } |
1276 | |
1277 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: " |
1278 | "UTOPPY_RESP_FILE_DATA: offset %lld, bytes left %ld\n" , |
1279 | device_xname(sc->sc_dev), off, (u_long)sc->sc_in_len)); |
1280 | break; |
1281 | |
1282 | case UTOPPY_RESP_FILE_END: |
1283 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: " |
1284 | "UTOPPY_RESP_FILE_END: sending ACK\n" , |
1285 | device_xname(sc->sc_dev))); |
1286 | UTOPPY_OUT_INIT(sc); |
1287 | utoppy_send_packet(sc, UTOPPY_CMD_ACK, UTOPPY_SHORT_TIMEOUT); |
1288 | /*FALLTHROUGH*/ |
1289 | |
1290 | case UTOPPY_RESP_SUCCESS: |
1291 | sc->sc_state = UTOPPY_STATE_IDLE; |
1292 | (void) utoppy_turbo_mode(sc, 0); |
1293 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: all " |
1294 | "done\n" , device_xname(sc->sc_dev))); |
1295 | break; |
1296 | |
1297 | case UTOPPY_RESP_ERROR: |
1298 | default: |
1299 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: bad " |
1300 | "response code 0x%0x\n" , device_xname(sc->sc_dev), resp)); |
1301 | utoppy_cancel(sc); |
1302 | return EIO; |
1303 | } |
1304 | |
1305 | return 0; |
1306 | } |
1307 | |
1308 | int |
1309 | utoppyopen(dev_t dev, int flag, int mode, |
1310 | struct lwp *l) |
1311 | { |
1312 | struct utoppy_softc *sc; |
1313 | int error = 0; |
1314 | |
1315 | sc = device_lookup_private(&utoppy_cd, UTOPPYUNIT(dev)); |
1316 | if (sc == NULL) |
1317 | return ENXIO; |
1318 | |
1319 | if (sc == NULL || sc->sc_iface == NULL || sc->sc_dying) |
1320 | return ENXIO; |
1321 | |
1322 | if (sc->sc_state != UTOPPY_STATE_CLOSED) { |
1323 | DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: already open\n" , |
1324 | device_xname(sc->sc_dev))); |
1325 | return EBUSY; |
1326 | } |
1327 | |
1328 | DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: opening...\n" , |
1329 | device_xname(sc->sc_dev))); |
1330 | |
1331 | sc->sc_refcnt++; |
1332 | sc->sc_state = UTOPPY_STATE_OPENING; |
1333 | sc->sc_turbo_mode = 0; |
1334 | sc->sc_out_data = kmem_alloc(UTOPPY_BSIZE + 1, KM_SLEEP); |
1335 | if (sc->sc_out_data == NULL) { |
1336 | error = ENOMEM; |
1337 | goto error; |
1338 | } |
1339 | |
1340 | sc->sc_in_data = kmem_alloc(UTOPPY_BSIZE + 1, KM_SLEEP); |
1341 | if (sc->sc_in_data == NULL) { |
1342 | kmem_free(sc->sc_out_data, UTOPPY_BSIZE + 1); |
1343 | sc->sc_out_data = NULL; |
1344 | error = ENOMEM; |
1345 | goto error; |
1346 | } |
1347 | |
1348 | if ((error = utoppy_cancel(sc)) != 0) |
1349 | goto error; |
1350 | |
1351 | if ((error = utoppy_check_ready(sc)) != 0) { |
1352 | DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: utoppy_check_ready()" |
1353 | " returned %d\n" , device_xname(sc->sc_dev), error)); |
1354 | } |
1355 | |
1356 | error: |
1357 | sc->sc_state = error ? UTOPPY_STATE_CLOSED : UTOPPY_STATE_IDLE; |
1358 | |
1359 | DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: done. error %d, new state " |
1360 | "'%s'\n" , device_xname(sc->sc_dev), error, |
1361 | utoppy_state_string(sc->sc_state))); |
1362 | |
1363 | if (--sc->sc_refcnt < 0) |
1364 | usb_detach_wakeupold(sc->sc_dev); |
1365 | |
1366 | return error; |
1367 | } |
1368 | |
1369 | int |
1370 | utoppyclose(dev_t dev, int flag, int mode, struct lwp *l) |
1371 | { |
1372 | struct utoppy_softc *sc; |
1373 | usbd_status err; |
1374 | |
1375 | sc = device_lookup_private(&utoppy_cd, UTOPPYUNIT(dev)); |
1376 | |
1377 | DPRINTF(UTOPPY_DBG_CLOSE, ("%s: utoppyclose: closing...\n" , |
1378 | device_xname(sc->sc_dev))); |
1379 | |
1380 | if (sc->sc_state < UTOPPY_STATE_IDLE) { |
1381 | /* We are being forced to close before the open completed. */ |
1382 | DPRINTF(UTOPPY_DBG_CLOSE, ("%s: utoppyclose: not properly " |
1383 | "open: %s\n" , device_xname(sc->sc_dev), |
1384 | utoppy_state_string(sc->sc_state))); |
1385 | return 0; |
1386 | } |
1387 | |
1388 | if (sc->sc_out_data) |
1389 | (void) utoppy_cancel(sc); |
1390 | |
1391 | if (sc->sc_out_pipe != NULL) { |
1392 | if ((err = usbd_abort_pipe(sc->sc_out_pipe)) != 0) |
1393 | printf("usbd_abort_pipe(OUT) returned %d\n" , err); |
1394 | sc->sc_out_pipe = NULL; |
1395 | } |
1396 | |
1397 | if (sc->sc_in_pipe != NULL) { |
1398 | if ((err = usbd_abort_pipe(sc->sc_in_pipe)) != 0) |
1399 | printf("usbd_abort_pipe(IN) returned %d\n" , err); |
1400 | sc->sc_in_pipe = NULL; |
1401 | } |
1402 | |
1403 | if (sc->sc_out_data) { |
1404 | kmem_free(sc->sc_out_data, UTOPPY_BSIZE + 1); |
1405 | sc->sc_out_data = NULL; |
1406 | } |
1407 | |
1408 | if (sc->sc_in_data) { |
1409 | kmem_free(sc->sc_in_data, UTOPPY_BSIZE + 1); |
1410 | sc->sc_in_data = NULL; |
1411 | } |
1412 | |
1413 | sc->sc_state = UTOPPY_STATE_CLOSED; |
1414 | |
1415 | DPRINTF(UTOPPY_DBG_CLOSE, ("%s: utoppyclose: done.\n" , |
1416 | device_xname(sc->sc_dev))); |
1417 | |
1418 | return 0; |
1419 | } |
1420 | |
1421 | int |
1422 | utoppyread(dev_t dev, struct uio *uio, int flags) |
1423 | { |
1424 | struct utoppy_softc *sc; |
1425 | struct utoppy_dirent ud; |
1426 | size_t len; |
1427 | int err; |
1428 | |
1429 | sc = device_lookup_private(&utoppy_cd, UTOPPYUNIT(dev)); |
1430 | |
1431 | if (sc->sc_dying) |
1432 | return EIO; |
1433 | |
1434 | sc->sc_refcnt++; |
1435 | |
1436 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: reading: state '%s'\n" , |
1437 | device_xname(sc->sc_dev), utoppy_state_string(sc->sc_state))); |
1438 | |
1439 | switch (sc->sc_state) { |
1440 | case UTOPPY_STATE_READDIR: |
1441 | err = 0; |
1442 | while (err == 0 && uio->uio_resid >= sizeof(ud) && |
1443 | sc->sc_state != UTOPPY_STATE_IDLE) { |
1444 | if (utoppy_readdir_decode(sc, &ud) == 0) |
1445 | err = utoppy_readdir_next(sc); |
1446 | else |
1447 | if ((err = uiomove(&ud, sizeof(ud), uio)) != 0) |
1448 | utoppy_cancel(sc); |
1449 | } |
1450 | break; |
1451 | |
1452 | case UTOPPY_STATE_READFILE: |
1453 | err = 0; |
1454 | while (err == 0 && uio->uio_resid > 0 && |
1455 | sc->sc_state != UTOPPY_STATE_IDLE) { |
1456 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: READFILE: " |
1457 | "resid %ld, bytes_left %ld\n" , |
1458 | device_xname(sc->sc_dev), (u_long)uio->uio_resid, |
1459 | (u_long)sc->sc_in_len)); |
1460 | |
1461 | if (sc->sc_in_len == 0 && |
1462 | (err = utoppy_readfile_next(sc)) != 0) { |
1463 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: " |
1464 | "READFILE: utoppy_readfile_next returned " |
1465 | "%d\n" , device_xname(sc->sc_dev), err)); |
1466 | break; |
1467 | } |
1468 | |
1469 | len = min(uio->uio_resid, sc->sc_in_len); |
1470 | if (len) { |
1471 | err = uiomove(UTOPPY_IN_DATA(sc), len, uio); |
1472 | if (err == 0) { |
1473 | sc->sc_in_offset += len; |
1474 | sc->sc_in_len -= len; |
1475 | } |
1476 | } |
1477 | } |
1478 | break; |
1479 | |
1480 | case UTOPPY_STATE_IDLE: |
1481 | err = 0; |
1482 | break; |
1483 | |
1484 | case UTOPPY_STATE_WRITEFILE: |
1485 | err = EBUSY; |
1486 | break; |
1487 | |
1488 | default: |
1489 | err = EIO; |
1490 | break; |
1491 | } |
1492 | |
1493 | DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: done. err %d, state '%s'\n" , |
1494 | device_xname(sc->sc_dev), err, utoppy_state_string(sc->sc_state))); |
1495 | |
1496 | if (--sc->sc_refcnt < 0) |
1497 | usb_detach_wakeupold(sc->sc_dev); |
1498 | |
1499 | return err; |
1500 | } |
1501 | |
1502 | int |
1503 | utoppywrite(dev_t dev, struct uio *uio, int flags) |
1504 | { |
1505 | struct utoppy_softc *sc; |
1506 | uint16_t resp; |
1507 | size_t len; |
1508 | int err; |
1509 | |
1510 | sc = device_lookup_private(&utoppy_cd, UTOPPYUNIT(dev)); |
1511 | |
1512 | if (sc->sc_dying) |
1513 | return EIO; |
1514 | |
1515 | switch(sc->sc_state) { |
1516 | case UTOPPY_STATE_WRITEFILE: |
1517 | break; |
1518 | |
1519 | case UTOPPY_STATE_IDLE: |
1520 | return 0; |
1521 | |
1522 | default: |
1523 | return EIO; |
1524 | } |
1525 | |
1526 | sc->sc_refcnt++; |
1527 | err = 0; |
1528 | |
1529 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: PRE-WRITEFILE: resid " |
1530 | "%ld, wr_size %lld, wr_offset %lld\n" , device_xname(sc->sc_dev), |
1531 | (u_long)uio->uio_resid, sc->sc_wr_size, sc->sc_wr_offset)); |
1532 | |
1533 | while (sc->sc_state == UTOPPY_STATE_WRITEFILE && |
1534 | (len = min(uio->uio_resid, sc->sc_wr_size)) != 0) { |
1535 | |
1536 | len = min(len, UTOPPY_BSIZE - (UTOPPY_HEADER_SIZE + |
1537 | sizeof(uint64_t) + 3)); |
1538 | |
1539 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: uiomove(%ld)\n" , |
1540 | device_xname(sc->sc_dev), (u_long)len)); |
1541 | |
1542 | UTOPPY_OUT_INIT(sc); |
1543 | utoppy_add_64(sc, sc->sc_wr_offset); |
1544 | |
1545 | err = uiomove(utoppy_current_ptr(sc->sc_out_data), len, uio); |
1546 | if (err) { |
1547 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: uiomove()" |
1548 | " returned %d\n" , device_xname(sc->sc_dev), err)); |
1549 | break; |
1550 | } |
1551 | |
1552 | utoppy_advance_ptr(sc->sc_out_data, len); |
1553 | |
1554 | err = utoppy_command(sc, UTOPPY_RESP_FILE_DATA, |
1555 | UTOPPY_LONG_TIMEOUT, &resp); |
1556 | if (err) { |
1557 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: " |
1558 | "utoppy_command(UTOPPY_RESP_FILE_DATA) " |
1559 | "returned %d\n" , device_xname(sc->sc_dev), err)); |
1560 | break; |
1561 | } |
1562 | if (resp != UTOPPY_RESP_SUCCESS) { |
1563 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: " |
1564 | "utoppy_command(UTOPPY_RESP_FILE_DATA) returned " |
1565 | "bad response 0x%x\n" , device_xname(sc->sc_dev), |
1566 | resp)); |
1567 | utoppy_cancel(sc); |
1568 | err = EIO; |
1569 | break; |
1570 | } |
1571 | |
1572 | sc->sc_wr_offset += len; |
1573 | sc->sc_wr_size -= len; |
1574 | } |
1575 | |
1576 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: POST-WRITEFILE: resid " |
1577 | "%ld, wr_size %lld, wr_offset %lld, err %d\n" , |
1578 | device_xname(sc->sc_dev), (u_long)uio->uio_resid, sc->sc_wr_size, |
1579 | sc->sc_wr_offset, err)); |
1580 | |
1581 | if (err == 0 && sc->sc_wr_size == 0) { |
1582 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: sending " |
1583 | "FILE_END...\n" , device_xname(sc->sc_dev))); |
1584 | UTOPPY_OUT_INIT(sc); |
1585 | err = utoppy_command(sc, UTOPPY_RESP_FILE_END, |
1586 | UTOPPY_LONG_TIMEOUT, &resp); |
1587 | if (err) { |
1588 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: " |
1589 | "utoppy_command(UTOPPY_RESP_FILE_END) returned " |
1590 | "%d\n" , device_xname(sc->sc_dev), err)); |
1591 | |
1592 | utoppy_cancel(sc); |
1593 | } |
1594 | |
1595 | sc->sc_state = UTOPPY_STATE_IDLE; |
1596 | DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: state %s\n" , |
1597 | device_xname(sc->sc_dev), |
1598 | utoppy_state_string(sc->sc_state))); |
1599 | } |
1600 | |
1601 | if (--sc->sc_refcnt < 0) |
1602 | usb_detach_wakeupold(sc->sc_dev); |
1603 | |
1604 | return err; |
1605 | } |
1606 | |
1607 | int |
1608 | utoppyioctl(dev_t dev, u_long cmd, void *data, int flag, |
1609 | struct lwp *l) |
1610 | { |
1611 | struct utoppy_softc *sc; |
1612 | struct utoppy_rename *ur; |
1613 | struct utoppy_readfile *urf; |
1614 | struct utoppy_writefile *uw; |
1615 | char uwf[UTOPPY_MAX_FILENAME_LEN + 1], *uwfp; |
1616 | uint16_t resp; |
1617 | int err; |
1618 | |
1619 | sc = device_lookup_private(&utoppy_cd, UTOPPYUNIT(dev)); |
1620 | |
1621 | if (sc->sc_dying) |
1622 | return EIO; |
1623 | |
1624 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: cmd 0x%08lx, state '%s'\n" , |
1625 | device_xname(sc->sc_dev), cmd, utoppy_state_string(sc->sc_state))); |
1626 | |
1627 | if (sc->sc_state != UTOPPY_STATE_IDLE && cmd != UTOPPYIOCANCEL) { |
1628 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: still busy.\n" , |
1629 | device_xname(sc->sc_dev))); |
1630 | return EBUSY; |
1631 | } |
1632 | |
1633 | sc->sc_refcnt++; |
1634 | |
1635 | switch (cmd) { |
1636 | case UTOPPYIOTURBO: |
1637 | err = 0; |
1638 | sc->sc_turbo_mode = *((int *)data) ? 1 : 0; |
1639 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOTURBO: " |
1640 | "%s\n" , device_xname(sc->sc_dev), |
1641 | sc->sc_turbo_mode ? "On" : "Off" )); |
1642 | break; |
1643 | |
1644 | case UTOPPYIOCANCEL: |
1645 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOCANCEL\n" , |
1646 | device_xname(sc->sc_dev))); |
1647 | err = utoppy_cancel(sc); |
1648 | break; |
1649 | |
1650 | case UTOPPYIOREBOOT: |
1651 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOREBOOT\n" , |
1652 | device_xname(sc->sc_dev))); |
1653 | UTOPPY_OUT_INIT(sc); |
1654 | err = utoppy_command(sc, UTOPPY_CMD_RESET, UTOPPY_LONG_TIMEOUT, |
1655 | &resp); |
1656 | if (err) |
1657 | break; |
1658 | |
1659 | if (resp != UTOPPY_RESP_SUCCESS) |
1660 | err = EIO; |
1661 | break; |
1662 | |
1663 | case UTOPPYIOSTATS: |
1664 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOSTATS\n" , |
1665 | device_xname(sc->sc_dev))); |
1666 | err = utoppy_stats(sc, (struct utoppy_stats *)data); |
1667 | break; |
1668 | |
1669 | case UTOPPYIORENAME: |
1670 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIORENAME\n" , |
1671 | device_xname(sc->sc_dev))); |
1672 | ur = (struct utoppy_rename *)data; |
1673 | UTOPPY_OUT_INIT(sc); |
1674 | |
1675 | if ((err = utoppy_add_path(sc, ur->ur_old_path, 1)) != 0) |
1676 | break; |
1677 | if ((err = utoppy_add_path(sc, ur->ur_new_path, 1)) != 0) |
1678 | break; |
1679 | |
1680 | err = utoppy_command(sc, UTOPPY_CMD_RENAME, |
1681 | UTOPPY_LONG_TIMEOUT, &resp); |
1682 | if (err) |
1683 | break; |
1684 | |
1685 | if (resp != UTOPPY_RESP_SUCCESS) |
1686 | err = EIO; |
1687 | break; |
1688 | |
1689 | case UTOPPYIOMKDIR: |
1690 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOMKDIR\n" , |
1691 | device_xname(sc->sc_dev))); |
1692 | UTOPPY_OUT_INIT(sc); |
1693 | err = utoppy_add_path(sc, *((const char **)data), 1); |
1694 | if (err) |
1695 | break; |
1696 | |
1697 | err = utoppy_command(sc, UTOPPY_CMD_MKDIR, UTOPPY_LONG_TIMEOUT, |
1698 | &resp); |
1699 | if (err) |
1700 | break; |
1701 | |
1702 | if (resp != UTOPPY_RESP_SUCCESS) |
1703 | err = EIO; |
1704 | break; |
1705 | |
1706 | case UTOPPYIODELETE: |
1707 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIODELETE\n" , |
1708 | device_xname(sc->sc_dev))); |
1709 | UTOPPY_OUT_INIT(sc); |
1710 | err = utoppy_add_path(sc, *((const char **)data), 0); |
1711 | if (err) |
1712 | break; |
1713 | |
1714 | err = utoppy_command(sc, UTOPPY_CMD_DELETE, UTOPPY_LONG_TIMEOUT, |
1715 | &resp); |
1716 | if (err) |
1717 | break; |
1718 | |
1719 | if (resp != UTOPPY_RESP_SUCCESS) |
1720 | err = EIO; |
1721 | break; |
1722 | |
1723 | case UTOPPYIOREADDIR: |
1724 | DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOREADDIR\n" , |
1725 | device_xname(sc->sc_dev))); |
1726 | UTOPPY_OUT_INIT(sc); |
1727 | err = utoppy_add_path(sc, *((const char **)data), 0); |
1728 | if (err) { |
1729 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppyioctl: " |
1730 | "utoppy_add_path() returned %d\n" , |
1731 | device_xname(sc->sc_dev), err)); |
1732 | break; |
1733 | } |
1734 | |
1735 | err = utoppy_send_packet(sc, UTOPPY_CMD_READDIR, |
1736 | UTOPPY_LONG_TIMEOUT); |
1737 | if (err != 0) { |
1738 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppyioctl: " |
1739 | "UTOPPY_CMD_READDIR returned %d\n" , |
1740 | device_xname(sc->sc_dev), err)); |
1741 | break; |
1742 | } |
1743 | |
1744 | err = utoppy_readdir_next(sc); |
1745 | if (err) { |
1746 | DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppyioctl: " |
1747 | "utoppy_readdir_next() returned %d\n" , |
1748 | device_xname(sc->sc_dev), err)); |
1749 | } |
1750 | break; |
1751 | |
1752 | case UTOPPYIOREADFILE: |
1753 | urf = (struct utoppy_readfile *)data; |
1754 | |
1755 | DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: UTOPPYIOREADFILE " |
1756 | "%s, offset %lld\n" , device_xname(sc->sc_dev), |
1757 | urf->ur_path, urf->ur_offset)); |
1758 | |
1759 | if ((err = utoppy_turbo_mode(sc, sc->sc_turbo_mode)) != 0) |
1760 | break; |
1761 | |
1762 | UTOPPY_OUT_INIT(sc); |
1763 | utoppy_add_8(sc, UTOPPY_FILE_READ); |
1764 | |
1765 | if ((err = utoppy_add_path(sc, urf->ur_path, 1)) != 0) |
1766 | break; |
1767 | |
1768 | utoppy_add_64(sc, urf->ur_offset); |
1769 | |
1770 | sc->sc_state = UTOPPY_STATE_READFILE; |
1771 | sc->sc_in_offset = 0; |
1772 | |
1773 | err = utoppy_send_packet(sc, UTOPPY_CMD_FILE, |
1774 | UTOPPY_LONG_TIMEOUT); |
1775 | if (err == 0) |
1776 | err = utoppy_readfile_next(sc); |
1777 | break; |
1778 | |
1779 | case UTOPPYIOWRITEFILE: |
1780 | uw = (struct utoppy_writefile *)data; |
1781 | |
1782 | DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: UTOPPYIOWRITEFILE " |
1783 | "%s, size %lld, offset %lld\n" , device_xname(sc->sc_dev), |
1784 | uw->uw_path, uw->uw_size, uw->uw_offset)); |
1785 | |
1786 | if ((err = utoppy_turbo_mode(sc, sc->sc_turbo_mode)) != 0) |
1787 | break; |
1788 | |
1789 | UTOPPY_OUT_INIT(sc); |
1790 | utoppy_add_8(sc, UTOPPY_FILE_WRITE); |
1791 | uwfp = utoppy_current_ptr(sc->sc_out_data); |
1792 | |
1793 | if ((err = utoppy_add_path(sc, uw->uw_path, 1)) != 0) { |
1794 | DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: add_path()" |
1795 | " returned %d\n" , device_xname(sc->sc_dev), err)); |
1796 | break; |
1797 | } |
1798 | |
1799 | strncpy(uwf, &uwfp[2], sizeof(uwf)); |
1800 | utoppy_add_64(sc, uw->uw_offset); |
1801 | |
1802 | err = utoppy_command(sc, UTOPPY_CMD_FILE, UTOPPY_LONG_TIMEOUT, |
1803 | &resp); |
1804 | if (err) { |
1805 | DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: " |
1806 | "utoppy_command(UTOPPY_CMD_FILE) returned " |
1807 | "%d\n" , device_xname(sc->sc_dev), err)); |
1808 | break; |
1809 | } |
1810 | if (resp != UTOPPY_RESP_SUCCESS) { |
1811 | DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: " |
1812 | "utoppy_command(UTOPPY_CMD_FILE) returned " |
1813 | "bad response 0x%x\n" , device_xname(sc->sc_dev), |
1814 | resp)); |
1815 | err = EIO; |
1816 | break; |
1817 | } |
1818 | |
1819 | UTOPPY_OUT_INIT(sc); |
1820 | utoppy_timestamp_encode(sc, uw->uw_mtime); |
1821 | utoppy_add_8(sc, UTOPPY_FTYPE_FILE); |
1822 | utoppy_add_64(sc, uw->uw_size); |
1823 | utoppy_add_string(sc, uwf, sizeof(uwf)); |
1824 | utoppy_add_32(sc, 0); |
1825 | |
1826 | err = utoppy_command(sc, UTOPPY_RESP_FILE_HEADER, |
1827 | UTOPPY_LONG_TIMEOUT, &resp); |
1828 | if (err) { |
1829 | DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: " |
1830 | "utoppy_command(UTOPPY_RESP_FILE_HEADER) " |
1831 | "returned %d\n" , device_xname(sc->sc_dev), err)); |
1832 | break; |
1833 | } |
1834 | if (resp != UTOPPY_RESP_SUCCESS) { |
1835 | DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: " |
1836 | "utoppy_command(UTOPPY_RESP_FILE_HEADER) " |
1837 | "returned bad response 0x%x\n" , |
1838 | device_xname(sc->sc_dev), resp)); |
1839 | err = EIO; |
1840 | break; |
1841 | } |
1842 | |
1843 | sc->sc_wr_offset = uw->uw_offset; |
1844 | sc->sc_wr_size = uw->uw_size; |
1845 | sc->sc_state = UTOPPY_STATE_WRITEFILE; |
1846 | |
1847 | DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: Changing state to " |
1848 | "%s. wr_offset %lld, wr_size %lld\n" , |
1849 | device_xname(sc->sc_dev), utoppy_state_string(sc->sc_state), |
1850 | sc->sc_wr_offset, sc->sc_wr_size)); |
1851 | break; |
1852 | |
1853 | default: |
1854 | DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: Invalid cmd\n" , |
1855 | device_xname(sc->sc_dev))); |
1856 | err = ENODEV; |
1857 | break; |
1858 | } |
1859 | |
1860 | DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: done. err %d, state '%s'\n" , |
1861 | device_xname(sc->sc_dev), err, utoppy_state_string(sc->sc_state))); |
1862 | |
1863 | if (err) |
1864 | utoppy_cancel(sc); |
1865 | |
1866 | if (--sc->sc_refcnt < 0) |
1867 | usb_detach_wakeupold(sc->sc_dev); |
1868 | |
1869 | return err; |
1870 | } |
1871 | |