xref: /haiku/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp (revision 0fe022fd0d6d600863555f5a556ab5a5273d20e0)
1 /*
2  * Copyright 2008-2012, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Lotz <mmlr@mlotz.ch>
7  */
8 
9 
10 #include "usb_disk.h"
11 
12 #include <ByteOrder.h>
13 #include <Drivers.h>
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include <fs/devfs.h>
20 
21 #include "scsi_sense.h"
22 #include "usb_disk_scsi.h"
23 
24 
25 #define DRIVER_NAME			"usb_disk"
26 #define DEVICE_NAME_BASE	"disk/usb/"
27 #define DEVICE_NAME			DEVICE_NAME_BASE"%" B_PRIu32 "/%d/raw"
28 
29 
30 //#define TRACE_USB_DISK
31 #ifdef TRACE_USB_DISK
32 #define TRACE(x...)			dprintf(DRIVER_NAME ": " x)
33 #define TRACE_ALWAYS(x...)	dprintf(DRIVER_NAME ": " x)
34 #else
35 #define TRACE(x...)			/* nothing */
36 #define TRACE_ALWAYS(x...)	dprintf(DRIVER_NAME ": " x)
37 #endif
38 
39 
40 int32 api_version = B_CUR_DRIVER_API_VERSION;
41 static usb_module_info *gUSBModule = NULL;
42 static disk_device *gDeviceList = NULL;
43 static uint32 gDeviceCount = 0;
44 static uint32 gLunCount = 0;
45 static mutex gDeviceListLock;
46 static char **gDeviceNames = NULL;
47 
48 static const uint8 kDeviceIcon[] = {
49 	0x6e, 0x63, 0x69, 0x66, 0x0a, 0x04, 0x01, 0x73, 0x05, 0x01, 0x02, 0x01,
50 	0x06, 0x02, 0xb1, 0xf8, 0x5d, 0x3a, 0x2f, 0xbf, 0xbe, 0xdb, 0x67, 0xb6,
51 	0x98, 0x06, 0x4b, 0x22, 0x15, 0x47, 0x13, 0x02, 0x00, 0xed, 0xed, 0xed,
52 	0xff, 0xab, 0xbc, 0xc6, 0x02, 0x01, 0x06, 0x02, 0xb9, 0x82, 0x56, 0x32,
53 	0x7d, 0xfb, 0xb8, 0x06, 0x39, 0xbe, 0xd9, 0xb5, 0x4b, 0x7d, 0x31, 0x4a,
54 	0xa4, 0xe7, 0x00, 0xd1, 0xde, 0xe4, 0xff, 0x7a, 0x9c, 0xae, 0x02, 0x00,
55 	0x16, 0x02, 0x38, 0xe9, 0xaa, 0x3b, 0x7b, 0x1d, 0xbf, 0xb0, 0xa6, 0x3d,
56 	0x16, 0x76, 0x4b, 0x84, 0x81, 0x48, 0x37, 0x36, 0x00, 0x99, 0xff, 0x53,
57 	0x02, 0x00, 0x16, 0x02, 0xba, 0x38, 0x9a, 0xb8, 0xef, 0x79, 0x3e, 0x34,
58 	0x8b, 0xbf, 0x56, 0x52, 0x48, 0x2c, 0x61, 0x4c, 0x4e, 0xec, 0x00, 0x40,
59 	0xff, 0x01, 0x05, 0xff, 0x05, 0x46, 0x02, 0x01, 0x16, 0x02, 0x35, 0xc2,
60 	0x71, 0x3a, 0xf6, 0x84, 0xb9, 0xf3, 0x5b, 0x34, 0x81, 0xa0, 0x49, 0xc0,
61 	0x57, 0x49, 0x6e, 0x51, 0xff, 0xf3, 0x00, 0x52, 0x02, 0x01, 0x06, 0x02,
62 	0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x80, 0x00,
63 	0x49, 0xa0, 0x00, 0x49, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x06,
64 	0xe3, 0x06, 0x0c, 0x06, 0x09, 0xab, 0xaa, 0x03, 0x3e, 0x5e, 0x3c, 0x5f,
65 	0x3e, 0x5e, 0x60, 0x4d, 0x5a, 0x4a, 0x60, 0x47, 0x56, 0x42, 0x50, 0x45,
66 	0x4c, 0x43, 0x2a, 0x54, 0x36, 0x5d, 0x36, 0x5d, 0x3a, 0x60, 0x06, 0x08,
67 	0xfb, 0xea, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x37, 0x59,
68 	0x37, 0x59, 0x38, 0x5a, 0x3b, 0x59, 0x3a, 0x5a, 0x3b, 0x59, 0x58, 0x3c,
69 	0x52, 0x36, 0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x06, 0x05,
70 	0xab, 0x03, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x52, 0x36,
71 	0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x0a, 0x05, 0xc2, 0x1c,
72 	0xb8, 0xf9, 0x4f, 0x25, 0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x51, 0x39,
73 	0x0a, 0x04, 0xc5, 0x50, 0xbb, 0xc0, 0xc2, 0x1c, 0xb8, 0xf9, 0x4f, 0x25,
74 	0xc9, 0x4c, 0xb7, 0xc4, 0x0a, 0x04, 0x51, 0x39, 0xc5, 0x50, 0xbb, 0xc0,
75 	0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x0a, 0x04, 0x4f, 0x2f, 0x51, 0x31,
76 	0x53, 0x2f, 0x51, 0x2d, 0x06, 0x04, 0xee, 0x4f, 0x35, 0x53, 0x30, 0x51,
77 	0x32, 0x55, 0x2e, 0x58, 0x2c, 0x54, 0x31, 0x56, 0x2f, 0x52, 0x33, 0x06,
78 	0x04, 0xee, 0x31, 0x58, 0x40, 0x47, 0x39, 0x4e, 0x47, 0x40, 0x50, 0x38,
79 	0x41, 0x48, 0x48, 0x41, 0x3a, 0x4f, 0x08, 0x02, 0x3a, 0x40, 0x3e, 0x3c,
80 	0x02, 0x04, 0x3e, 0x3a, 0xbe, 0x48, 0x3a, 0xbf, 0x9f, 0x3a, 0x41, 0x3d,
81 	0x41, 0xbd, 0xe2, 0x41, 0xbf, 0x39, 0x3e, 0x40, 0xbf, 0x9f, 0x40, 0xbe,
82 	0x48, 0x40, 0x3b, 0x3d, 0x3b, 0xbf, 0x39, 0x3b, 0xbd, 0xe2, 0x06, 0x05,
83 	0xbe, 0x02, 0x32, 0x56, 0x36, 0x5a, 0x36, 0x5a, 0x37, 0x5b, 0x3a, 0x5a,
84 	0x39, 0x5b, 0x3a, 0x5a, 0x58, 0x3c, 0x52, 0x36, 0x11, 0x0a, 0x00, 0x01,
85 	0x00, 0x00, 0x0a, 0x01, 0x01, 0x03, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54,
86 	0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0,
87 	0xc5, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x02, 0x3f,
88 	0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6,
89 	0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x03, 0x01, 0x05, 0x02, 0x3f, 0xe9,
90 	0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d,
91 	0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x01, 0x12, 0x3f, 0xe9, 0x8e,
92 	0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0xb0, 0x64,
93 	0x46, 0x78, 0x3b, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x04, 0x01, 0x02,
94 	0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9,
95 	0x8e, 0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x05, 0x01, 0x0b, 0x02,
96 	0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e,
97 	0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f,
98 	0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6,
99 	0x5b, 0x2d, 0x45, 0x43, 0x93, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f, 0xe9,
100 	0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc7, 0x7d,
101 	0x8b, 0x44, 0x36, 0x9a, 0x0a, 0x06, 0x02, 0x07, 0x08, 0x02, 0x3f, 0xe9,
102 	0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d,
103 	0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x09, 0x12, 0x3f, 0x6c, 0x5c,
104 	0xba, 0xea, 0x46, 0x3a, 0xea, 0x46, 0x3f, 0x6c, 0x5c, 0xc5, 0x19, 0x6c,
105 	0x46, 0x6b, 0x36, 0x01, 0x17, 0x8c, 0x22, 0x04, 0x0a, 0x07, 0x01, 0x09,
106 	0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9,
107 	0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x01, 0x17, 0x88, 0x22, 0x04,
108 	0x0a, 0x08, 0x01, 0x09, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b,
109 	0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x01, 0xed, 0x46, 0x11, 0xa8, 0x01,
110 	0x17, 0x85, 0x22, 0x04, 0x0a, 0x01, 0x01, 0x0a, 0x12, 0x3f, 0xe9, 0x8e,
111 	0xbb, 0x54, 0xe2, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43, 0xc6, 0x59, 0xd0,
112 	0x46, 0xdb, 0x8c, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x06, 0x01, 0x0a,
113 	0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9,
114 	0x8e, 0xc6, 0x99, 0xc6, 0x45, 0x5e, 0x3a, 0x0a, 0x01, 0x01, 0x0a, 0x12,
115 	0x3f, 0x21, 0x43, 0xba, 0xaa, 0x52, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43,
116 	0xc7, 0xd2, 0xa7, 0x49, 0x5f, 0xed, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a,
117 	0x09, 0x01, 0x0a, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54,
118 	0xe2, 0x3f, 0xe9, 0x8e, 0xc8, 0xa5, 0xc8, 0x48, 0xeb, 0x05
119 };
120 
121 const unsigned char kCDIcon[] = {
122 	0x6e, 0x63, 0x69, 0x66, 0x05, 0x05, 0x00, 0x02, 0x03, 0x06, 0x05, 0xb8,
123 	0x12, 0xa5, 0xbe, 0x03, 0xe1, 0x3d, 0xe7, 0x84, 0xb8, 0x02, 0x10, 0x49,
124 	0xf7, 0x9f, 0x49, 0xed, 0xd8, 0x00, 0xf1, 0xf1, 0xf1, 0x36, 0xd9, 0xdd,
125 	0xf4, 0x8a, 0x99, 0x96, 0xb9, 0xb4, 0xb8, 0xbe, 0xdb, 0xff, 0xf4, 0xf4,
126 	0xf4, 0x04, 0xeb, 0xd0, 0x02, 0x00, 0x06, 0x02, 0x3c, 0x92, 0xc0, 0x38,
127 	0x8f, 0x5f, 0xb8, 0x54, 0x50, 0x3c, 0x57, 0x63, 0x48, 0xd8, 0xdf, 0x48,
128 	0x89, 0x5b, 0x00, 0x41, 0x37, 0xa9, 0xff, 0xb9, 0xb9, 0xb9, 0x04, 0x01,
129 	0x7e, 0x04, 0x02, 0x04, 0x3f, 0x2c, 0x4e, 0x2c, 0x30, 0x2c, 0x22, 0x40,
130 	0x22, 0x34, 0x22, 0x4c, 0x3f, 0x54, 0x30, 0x54, 0x4e, 0x54, 0x5c, 0x40,
131 	0x5c, 0x4c, 0x5c, 0x34, 0x02, 0x04, 0x3f, 0x3a, 0x43, 0x3a, 0x3b, 0x3a,
132 	0x39, 0x3e, 0x39, 0x3c, 0x39, 0x40, 0x3f, 0x42, 0x3b, 0x42, 0x43, 0x42,
133 	0x45, 0x3e, 0x45, 0x40, 0x45, 0x3c, 0x02, 0x04, 0x4b, 0x3e, 0x4b, 0x3a,
134 	0x4b, 0x42, 0x3f, 0x46, 0x47, 0x46, 0x37, 0x46, 0x33, 0x3e, 0x33, 0x42,
135 	0x33, 0x3a, 0x3f, 0xbb, 0xf7, 0x37, 0xbb, 0xf7, 0x47, 0xbb, 0xf7, 0x02,
136 	0x04, 0x40, 0x2a, 0x54, 0x2a, 0x50, 0x2c, 0x5c, 0x40, 0x5c, 0x34, 0x5c,
137 	0x4c, 0x40, 0x56, 0x50, 0x54, 0x54, 0x56, 0x60, 0x40, 0x60, 0x4c, 0x60,
138 	0x34, 0x06, 0x0a, 0x04, 0x01, 0x03, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x01,
139 	0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x00, 0x02, 0x00,
140 	0x01, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x00, 0x04, 0x0a, 0x01, 0x02,
141 	0x00, 0x02, 0x00, 0x0a, 0x02, 0x02, 0x02, 0x01, 0x00, 0x0a, 0x03, 0x01,
142 	0x02, 0x10, 0x01, 0x17, 0x82, 0x00, 0x04
143 };
144 
145 const unsigned char kMemoryStickIcon[] = {
146 	0x6e, 0x63, 0x69, 0x66, 0x09, 0x05, 0x00, 0x04, 0x00, 0x63, 0x02, 0x00,
147 	0x06, 0x02, 0x3a, 0x9c, 0xfb, 0x3b, 0x07, 0x44, 0xbd, 0x68, 0x2d, 0x3c,
148 	0xf0, 0x9a, 0x49, 0x8c, 0xd7, 0x4a, 0x02, 0xf0, 0x00, 0x67, 0x72, 0xc0,
149 	0xff, 0x5e, 0x68, 0xae, 0x02, 0x00, 0x06, 0x02, 0x39, 0xd8, 0xc8, 0x38,
150 	0x57, 0x6f, 0xbb, 0x1e, 0xa3, 0x3c, 0x90, 0x06, 0x4b, 0x3b, 0x28, 0x49,
151 	0x91, 0x9a, 0x00, 0x55, 0x55, 0x84, 0xff, 0x73, 0x79, 0xaa, 0x02, 0x00,
152 	0x06, 0x02, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
153 	0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xbf, 0xed,
154 	0xff, 0x81, 0x8b, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x3d, 0x00, 0x00, 0x00,
155 	0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x4a, 0x00, 0x00, 0xa1,
156 	0x24, 0x8d, 0x00, 0xbd, 0xc1, 0xe9, 0xfe, 0x83, 0x8d, 0xdc, 0x03, 0xc3,
157 	0xca, 0xff, 0x03, 0x08, 0x13, 0x47, 0x03, 0x08, 0x13, 0x47, 0x0d, 0x0a,
158 	0x04, 0x5e, 0xbc, 0x52, 0x3c, 0x5a, 0x42, 0x5a, 0x62, 0xbd, 0x1e, 0x0a,
159 	0x06, 0x22, 0x46, 0x22, 0x4b, 0x3c, 0x58, 0x5e, 0x36, 0x5e, 0x32, 0x46,
160 	0x2a, 0x06, 0x08, 0xfe, 0x9b, 0x3c, 0x52, 0xbc, 0xae, 0xc6, 0x3d, 0xbd,
161 	0x92, 0xc6, 0xa6, 0xbc, 0x79, 0xc6, 0x25, 0x37, 0x52, 0xbc, 0x66, 0xc7,
162 	0x08, 0xba, 0x74, 0xc6, 0x22, 0x26, 0x4a, 0xb7, 0x26, 0xc4, 0x8c, 0xb5,
163 	0x34, 0xc3, 0xa6, 0xb5, 0x9e, 0xc2, 0xfb, 0xb5, 0xc9, 0xc3, 0x0f, 0xb4,
164 	0x89, 0xc2, 0x7b, 0x22, 0x46, 0x4b, 0x3c, 0x58, 0x0a, 0x04, 0x3c, 0x52,
165 	0x5e, 0x32, 0x5e, 0x36, 0x3c, 0x58, 0x0a, 0x04, 0x3c, 0x52, 0x5e, 0x32,
166 	0x46, 0x2a, 0x22, 0x47, 0x06, 0x07, 0xaa, 0x39, 0xc4, 0x10, 0xbf, 0x1a,
167 	0xbc, 0xac, 0xc5, 0xbc, 0x37, 0x50, 0x38, 0x51, 0x50, 0xc4, 0xbc, 0xbe,
168 	0xcb, 0x40, 0x38, 0xbf, 0xe4, 0xbc, 0x7f, 0xbf, 0x8f, 0xbc, 0xa6, 0x06,
169 	0x08, 0xaa, 0xab, 0xb5, 0xeb, 0xc3, 0x48, 0xbe, 0x7b, 0xbc, 0x6a, 0xbe,
170 	0x67, 0xbb, 0xbb, 0xb5, 0x3f, 0xc3, 0x31, 0x26, 0x4a, 0xb5, 0x2e, 0xc3,
171 	0xa5, 0xb6, 0x74, 0xc4, 0x2f, 0x37, 0x52, 0x38, 0x51, 0x30, 0x4d, 0x0a,
172 	0x04, 0x38, 0x50, 0x4a, 0x3e, 0x3c, 0x38, 0xb5, 0xeb, 0xc3, 0x48, 0x06,
173 	0x0d, 0xf6, 0xff, 0xff, 0x03, 0xcc, 0x52, 0xbd, 0x0d, 0xbd, 0x16, 0xca,
174 	0xee, 0xbd, 0x16, 0xca, 0xee, 0xbd, 0x16, 0xca, 0x64, 0xbd, 0x01, 0xc9,
175 	0xf8, 0xbc, 0x36, 0xca, 0x10, 0xbc, 0xb6, 0xc9, 0xbc, 0xbb, 0xb5, 0xc8,
176 	0xf4, 0xbc, 0x36, 0xc8, 0xf4, 0xbb, 0xd9, 0xc9, 0x21, 0xbc, 0xb6, 0xc8,
177 	0x70, 0xbd, 0x16, 0xc8, 0xf7, 0xbd, 0x01, 0xc7, 0xff, 0xbd, 0x13, 0xc7,
178 	0xea, 0xbc, 0x36, 0xc7, 0xbd, 0xbc, 0xda, 0xc7, 0xea, 0xbb, 0xd9, 0xc6,
179 	0xd3, 0xbc, 0x36, 0xc7, 0x24, 0xbb, 0xbb, 0xc6, 0xa6, 0xbc, 0xb0, 0xc5,
180 	0xdb, 0xbd, 0x16, 0xc6, 0x56, 0xbc, 0xfb, 0xc5, 0xdb, 0xbd, 0x16, 0xc4,
181 	0x5f, 0xbd, 0x16, 0xc4, 0x5f, 0xbd, 0x16, 0xc5, 0x24, 0xbc, 0xcb, 0xc6,
182 	0x2f, 0xbb, 0xd9, 0xc5, 0xcf, 0xbc, 0x6c, 0xc7, 0xa8, 0xbb, 0x82, 0xca,
183 	0x94, 0xbb, 0xd9, 0xc9, 0x1e, 0xbb, 0x7c, 0xca, 0xfa, 0xbc, 0x69, 0xcc,
184 	0x52, 0xbd, 0x0d, 0xcb, 0x92, 0xbc, 0xcb, 0xcc, 0x52, 0xbd, 0x0d, 0x06,
185 	0x04, 0xfe, 0xcc, 0x52, 0xbd, 0x91, 0xcc, 0x52, 0xbd, 0x94, 0xcc, 0x52,
186 	0xbd, 0x94, 0xc9, 0xa7, 0xbd, 0xee, 0xc4, 0x5f, 0xbd, 0x91, 0xc7, 0x00,
187 	0xbd, 0xee, 0xc7, 0x12, 0xbd, 0x55, 0xcc, 0x52, 0xbd, 0x91, 0xc9, 0xb3,
188 	0xbd, 0x52, 0xcc, 0x52, 0xbd, 0x91, 0x02, 0x03, 0xc5, 0x75, 0xbe, 0x50,
189 	0xc5, 0x75, 0xbe, 0x50, 0xc7, 0x75, 0xbe, 0x24, 0xcb, 0x50, 0xbe, 0x50,
190 	0xc9, 0x63, 0xbe, 0x24, 0xc9, 0x5a, 0xbe, 0x92, 0xc5, 0x75, 0xbe, 0x50,
191 	0xc7, 0x66, 0xbe, 0x92, 0xc5, 0x75, 0xbe, 0x50, 0x02, 0x02, 0xc6, 0x2f,
192 	0xbe, 0xdd, 0xc7, 0xa5, 0xbf, 0x22, 0xc7, 0xae, 0xbe, 0xb3, 0xca, 0x97,
193 	0xbe, 0xdd, 0xc9, 0x24, 0xbe, 0xb0, 0xc9, 0x1e, 0xbf, 0x22, 0x0a, 0x03,
194 	0x45, 0x2d, 0x4a, 0x2f, 0x4a, 0x2c, 0x0a, 0x0a, 0x01, 0x01, 0x00, 0x00,
195 	0x0a, 0x00, 0x01, 0x01, 0x10, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02,
196 	0x01, 0x02, 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04,
197 	0x00, 0x0a, 0x03, 0x01, 0x06, 0x00, 0x0a, 0x06, 0x01, 0x05, 0x00, 0x0a,
198 	0x05, 0x01, 0x07, 0x00, 0x0a, 0x07, 0x04, 0x09, 0x0a, 0x0b, 0x08, 0x02,
199 	0xbe, 0x56, 0x60, 0x3e, 0x5e, 0xcf, 0xbe, 0x5e, 0xcf, 0xbe, 0x56, 0x60,
200 	0x4c, 0xe9, 0x91, 0x41, 0xdd, 0x95, 0x0a, 0x08, 0x01, 0x0c, 0x00
201 };
202 
203 
204 const unsigned char kSDIcon[] = {
205 	0x6e, 0x63, 0x69, 0x66, 0x09, 0x05, 0x00, 0x04, 0x00, 0x63, 0x02, 0x00,
206 	0x06, 0x02, 0x3a, 0x9c, 0xfb, 0x3b, 0x07, 0x44, 0xbd, 0x68, 0x2d, 0x3c,
207 	0xf0, 0x9a, 0x49, 0x8c, 0xd7, 0x4a, 0x02, 0xf0, 0x00, 0xa4, 0xa4, 0xa4,
208 	0xff, 0x5a, 0x5b, 0x65, 0x02, 0x00, 0x16, 0x02, 0x39, 0xd8, 0xc8, 0x38,
209 	0x57, 0x6f, 0xbb, 0x1e, 0xa3, 0x3c, 0x90, 0x06, 0x4b, 0x3b, 0x28, 0x49,
210 	0x91, 0x9a, 0x00, 0x55, 0xff, 0x39, 0x02, 0x00, 0x16, 0x02, 0x3d, 0x00,
211 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x4a, 0x00,
212 	0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xff, 0x5b, 0x02, 0x00, 0x06, 0x02,
213 	0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
214 	0x4a, 0x00, 0x00, 0xa1, 0x24, 0x8d, 0x00, 0xe5, 0xe7, 0xf9, 0xfe, 0xb3,
215 	0xb5, 0xc3, 0x05, 0xc6, 0x03, 0x08, 0x13, 0x47, 0x05, 0xff, 0x0e, 0x0a,
216 	0x04, 0x60, 0xc0, 0x4e, 0x44, 0x5e, 0x4a, 0x5e, 0x60, 0x47, 0x0a, 0x0c,
217 	0x22, 0x46, 0x22, 0x48, 0x22, 0x4b, 0x44, 0x5c, 0xca, 0x15, 0xc2, 0x2a,
218 	0x59, 0x45, 0x5b, 0x43, 0x5c, 0x44, 0x5e, 0x42, 0x5e, 0x3e, 0xcb, 0x07,
219 	0x33, 0x46, 0x2a, 0x06, 0x08, 0xfe, 0x9b, 0x44, 0x56, 0xbf, 0xde, 0xc7,
220 	0xd5, 0xc0, 0xc2, 0xc8, 0x3e, 0xbf, 0xa9, 0xc7, 0xbd, 0x3f, 0x56, 0xbf,
221 	0x96, 0xc8, 0xa0, 0xbd, 0xa4, 0xc7, 0xba, 0x26, 0x4a, 0xb7, 0x26, 0xc4,
222 	0x8c, 0xb5, 0x34, 0xc3, 0xa6, 0xb5, 0x9e, 0xc2, 0xfb, 0xb5, 0xc9, 0xc3,
223 	0x0f, 0xb4, 0x89, 0xc2, 0x7b, 0x22, 0x46, 0x4b, 0x44, 0x5c, 0x0a, 0x0c,
224 	0x44, 0x56, 0xca, 0x87, 0xbf, 0xe4, 0x5a, 0xbf, 0x83, 0x5b, 0xbf, 0x1d,
225 	0xca, 0xe7, 0xbf, 0x92, 0x5e, 0x3e, 0x5e, 0x42, 0xca, 0xeb, 0xc1, 0x54,
226 	0xca, 0x85, 0xc0, 0xf5, 0x5a, 0x44, 0xca, 0x86, 0xc1, 0xb9, 0x44, 0x5c,
227 	0x0a, 0x0d, 0x44, 0x56, 0xca, 0x8f, 0xbf, 0xf3, 0x5a, 0xbf, 0x93, 0xca,
228 	0x90, 0xbf, 0x22, 0xca, 0xf3, 0xbf, 0x9e, 0x5e, 0x3e, 0xcb, 0x09, 0xba,
229 	0x91, 0x46, 0x2a, 0xc0, 0x1e, 0xb8, 0x9c, 0x41, 0x2f, 0x3b, 0x34, 0xbd,
230 	0x91, 0xba, 0xab, 0x22, 0x47, 0x06, 0x07, 0xaa, 0x39, 0xc9, 0xa4, 0xbf,
231 	0x80, 0xc0, 0xa8, 0xc7, 0xba, 0x41, 0x55, 0x42, 0x56, 0x55, 0xca, 0x50,
232 	0xbf, 0x31, 0x44, 0x34, 0xc1, 0x7c, 0xba, 0xe7, 0xc1, 0x27, 0xbb, 0x0e,
233 	0x06, 0x08, 0xaa, 0xab, 0xb5, 0x1f, 0xc2, 0xe2, 0xbf, 0xad, 0xba, 0xd2,
234 	0xbf, 0x99, 0xba, 0x23, 0xb4, 0x73, 0xc2, 0xcb, 0x24, 0x49, 0xb4, 0x62,
235 	0xc3, 0x3f, 0xb5, 0xa8, 0xc3, 0xc9, 0x40, 0x57, 0x41, 0x56, 0x32, 0x4e,
236 	0x0a, 0x04, 0x40, 0x55, 0x58, 0x3f, 0x40, 0x33, 0xb5, 0x85, 0xc2, 0xe2,
237 	0x0a, 0x03, 0x44, 0x2d, 0x46, 0x2f, 0x42, 0x31, 0x0a, 0x08, 0x47, 0x30,
238 	0x47, 0x31, 0x49, 0x32, 0x4a, 0x32, 0x4a, 0x34, 0x49, 0x34, 0x47, 0x33,
239 	0x45, 0x32, 0x04, 0x0f, 0xaf, 0xff, 0xeb, 0x3f, 0xc9, 0x8a, 0xc3, 0xb2,
240 	0xc9, 0x8a, 0xc3, 0xb2, 0xc9, 0x8a, 0xc4, 0xc4, 0xc3, 0x50, 0xc4, 0xc4,
241 	0xc7, 0xed, 0xc4, 0xda, 0xbc, 0x23, 0xc4, 0xa6, 0xb6, 0x1e, 0xc3, 0xee,
242 	0xb3, 0x4d, 0xcb, 0xb1, 0xc1, 0x6c, 0xcc, 0x29, 0xba, 0x22, 0xcc, 0x29,
243 	0xcb, 0xd2, 0xcc, 0x29, 0xd5, 0xf5, 0xc5, 0xb4, 0xd4, 0x64, 0xcc, 0x92,
244 	0xd7, 0x12, 0xc0, 0xdb, 0xcd, 0xcd, 0xbd, 0x13, 0xd2, 0xd7, 0xbe, 0x92,
245 	0xca, 0x67, 0xbc, 0x11, 0xc4, 0x35, 0xb9, 0x75, 0xc4, 0x35, 0xba, 0xac,
246 	0xc4, 0x35, 0xb7, 0xe8, 0xcb, 0x40, 0xb7, 0xdc, 0xc8, 0x5e, 0xb7, 0xf3,
247 	0xce, 0x66, 0xb7, 0xc6, 0xda, 0x9a, 0xb7, 0xc2, 0xdc, 0x27, 0xb3, 0x5d,
248 	0xca, 0xaa, 0xb3, 0x8e, 0xd3, 0x61, 0xb3, 0x21, 0xbe, 0x1d, 0xb4, 0x2f,
249 	0xbb, 0x7a, 0xb9, 0xa2, 0xbb, 0xc5, 0xb6, 0xf8, 0xbb, 0x18, 0xbd, 0x17,
250 	0xc2, 0x95, 0xc0, 0x93, 0xbf, 0xc1, 0xbf, 0x7e, 0xc5, 0x46, 0xc1, 0x9a,
251 	0xc9, 0x8a, 0xc3, 0xb2, 0xc9, 0x8a, 0xc2, 0x5d, 0xc9, 0x8a, 0xc3, 0xb2,
252 	0x00, 0x0c, 0xeb, 0x0d, 0xbb, 0x77, 0xeb, 0x0d, 0xbb, 0x77, 0xea, 0xfe,
253 	0xbb, 0x59, 0xea, 0xd8, 0xbb, 0x1d, 0xea, 0xeb, 0xbb, 0x3b, 0xea, 0xd8,
254 	0xbb, 0x1d, 0xf9, 0xf9, 0xb7, 0x4e, 0xf9, 0xf9, 0xb7, 0x4e, 0xfc, 0x60,
255 	0xb8, 0x7a, 0xff, 0x6c, 0xbb, 0xeb, 0xfe, 0x62, 0xb9, 0xfc, 0xff, 0x80,
256 	0xbd, 0x9e, 0xff, 0x16, 0xc1, 0x17, 0xff, 0x80, 0xbf, 0x60, 0xf6, 0x6e,
257 	0xcb, 0xd7, 0xd7, 0xca, 0xcc, 0x22, 0xdf, 0xeb, 0xcb, 0xc4, 0xd7, 0xca,
258 	0xcc, 0x22, 0xdb, 0x52, 0xc2, 0x30, 0xdb, 0x52, 0xc2, 0x30, 0xdd, 0x79,
259 	0xc2, 0x1d, 0xe2, 0x70, 0xc1, 0x71, 0xe0, 0x94, 0xc1, 0xce, 0xe4, 0x48,
260 	0xc1, 0x13, 0xe7, 0x49, 0xc0, 0x05, 0xe5, 0xf0, 0xc0, 0x97, 0xe8, 0xa2,
261 	0xbf, 0x73, 0xea, 0x5c, 0xbe, 0x1d, 0xe9, 0xb0, 0xbe, 0xce, 0xeb, 0x05,
262 	0xbd, 0x6d, 0xeb, 0x36, 0xbb, 0xfe, 0xeb, 0x58, 0xbc, 0xb9, 0xeb, 0x2e,
263 	0xbb, 0xd1, 0xeb, 0x0d, 0xbb, 0x77, 0xeb, 0x1f, 0xbb, 0xa4, 0xeb, 0x0d,
264 	0xbb, 0x77, 0x04, 0x06, 0xff, 0x0b, 0xf6, 0xc4, 0xb5, 0xfc, 0xf6, 0xc4,
265 	0xb5, 0xfc, 0xf5, 0xe3, 0xb5, 0x9f, 0xf3, 0xc7, 0xb4, 0xee, 0xf4, 0xac,
266 	0xb5, 0x2a, 0xf3, 0xc7, 0xb4, 0xee, 0xe8, 0xb5, 0xb9, 0x66, 0xe8, 0xb5,
267 	0xb9, 0x66, 0xe9, 0x07, 0xb9, 0x8f, 0xe9, 0xc3, 0xba, 0x16, 0xe9, 0x83,
268 	0xb9, 0xed, 0xe9, 0xd5, 0xba, 0x25, 0xe9, 0xf7, 0xba, 0x3c, 0xe9, 0xe4,
269 	0xba, 0x31, 0xe9, 0xf7, 0xba, 0x3c, 0xf6, 0xc4, 0xb5, 0xfc, 0x04, 0x07,
270 	0xff, 0x2f, 0xed, 0xb0, 0xb3, 0xe8, 0xed, 0xb0, 0xb3, 0xe8, 0xeb, 0xdb,
271 	0xb3, 0xa5, 0xe9, 0x70, 0xb3, 0x7f, 0xea, 0x55, 0xb3, 0x87, 0xe5, 0xe8,
272 	0xb3, 0x69, 0xe0, 0x97, 0xb3, 0x5a, 0xe4, 0x84, 0xb3, 0x56, 0xe0, 0x97,
273 	0xb3, 0x5a, 0xdf, 0x06, 0xb7, 0xc6, 0xdf, 0x06, 0xb7, 0xc6, 0xdf, 0xfe,
274 	0xb7, 0xcd, 0xe2, 0x0e, 0xb7, 0xeb, 0xe0, 0x6e, 0xb7, 0xc2, 0xe3, 0x8d,
275 	0xb8, 0x11, 0xe6, 0x24, 0xb8, 0x9c, 0xe4, 0xed, 0xb8, 0x49, 0xe6, 0x24,
276 	0xb8, 0x9c, 0xed, 0xb0, 0xb3, 0xe8, 0x0d, 0x0a, 0x01, 0x01, 0x00, 0x00,
277 	0x0a, 0x00, 0x01, 0x01, 0x10, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02,
278 	0x01, 0x02, 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04,
279 	0x00, 0x0a, 0x03, 0x01, 0x06, 0x00, 0x0a, 0x06, 0x01, 0x05, 0x00, 0x0a,
280 	0x05, 0x01, 0x07, 0x00, 0x0a, 0x00, 0x01, 0x0a, 0x02, 0x3a, 0x19, 0xca,
281 	0x38, 0x20, 0xc9, 0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f,
282 	0x48, 0xbc, 0x82, 0x0a, 0x00, 0x01, 0x0b, 0x02, 0x3a, 0x19, 0xca, 0x38,
283 	0x20, 0xc9, 0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f, 0x48,
284 	0xbc, 0x82, 0x0a, 0x00, 0x01, 0x0c, 0x02, 0x3a, 0x19, 0xca, 0x38, 0x20,
285 	0xc9, 0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f, 0x48, 0xbc,
286 	0x82, 0x0a, 0x00, 0x01, 0x0d, 0x02, 0x3a, 0x19, 0xca, 0x38, 0x20, 0xc9,
287 	0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f, 0x48, 0xbc, 0x82,
288 	0x0a, 0x08, 0x02, 0x08, 0x09, 0x00
289 };
290 
291 const unsigned char kMobileIcon[] = {
292 	0x6e, 0x63, 0x69, 0x66, 0x09, 0x05, 0x00, 0x04, 0x00, 0x66, 0x02, 0x00,
293 	0x06, 0x02, 0x38, 0xf1, 0x86, 0x38, 0x62, 0x09, 0xba, 0xd4, 0x58, 0x3b,
294 	0x7e, 0xb7, 0x4a, 0x28, 0x8f, 0x47, 0xaf, 0x7c, 0x00, 0xee, 0xef, 0xf3,
295 	0xff, 0xce, 0xd2, 0xd9, 0x02, 0x00, 0x06, 0x03, 0x39, 0x3a, 0x47, 0xba,
296 	0x81, 0x7b, 0x3f, 0x5c, 0xe7, 0x3e, 0x2a, 0x74, 0xc5, 0xab, 0xe0, 0x49,
297 	0x3b, 0x67, 0x00, 0xff, 0xff, 0xff, 0x42, 0xd9, 0xde, 0xe5, 0xff, 0xb5,
298 	0xb8, 0xbf, 0x02, 0x00, 0x06, 0x02, 0x38, 0xdc, 0xc0, 0x32, 0xb6, 0x96,
299 	0xb9, 0xc3, 0xec, 0x3f, 0xf8, 0xe2, 0x4a, 0x00, 0x51, 0xb9, 0x4b, 0xdc,
300 	0x00, 0xce, 0xd2, 0xd9, 0xff, 0x95, 0x9a, 0xa6, 0x02, 0x03, 0x06, 0x04,
301 	0xba, 0x6c, 0x40, 0xb8, 0x55, 0xde, 0xbb, 0x7a, 0x07, 0x3d, 0x9b, 0x59,
302 	0x49, 0xea, 0xb7, 0x49, 0x0b, 0x3e, 0x20, 0xda, 0xde, 0xe5, 0x52, 0xff,
303 	0xff, 0xff, 0xa4, 0xff, 0xff, 0xff, 0xff, 0x95, 0x9a, 0xa6, 0x05, 0xff,
304 	0x02, 0x00, 0x06, 0x03, 0x38, 0x2d, 0xf0, 0x3a, 0x3d, 0xb3, 0xbf, 0x98,
305 	0xe5, 0x3d, 0x7f, 0x98, 0x4b, 0xc7, 0x2b, 0x44, 0x03, 0xbe, 0x00, 0x9e,
306 	0xc9, 0xff, 0xb2, 0x60, 0x99, 0xde, 0xff, 0x7f, 0xb0, 0xef, 0x03, 0x73,
307 	0x79, 0x80, 0x15, 0x0a, 0x0a, 0x29, 0x54, 0x39, 0x5e, 0x3c, 0x5c, 0x3d,
308 	0x5a, 0x46, 0x53, 0x47, 0x53, 0x59, 0x44, 0x59, 0x43, 0x4a, 0x3c, 0x29,
309 	0x52, 0x0a, 0x08, 0x4a, 0x39, 0x4b, 0x3a, 0x3a, 0x5c, 0x38, 0x5c, 0x27,
310 	0x52, 0x29, 0x4e, 0x2a, 0x4d, 0x38, 0x31, 0x0a, 0x0a, 0x4a, 0x39, 0x3b,
311 	0x57, 0x39, 0x59, 0x37, 0x59, 0x36, 0x5a, 0x29, 0x52, 0x27, 0x52, 0x29,
312 	0x4e, 0x2a, 0x4d, 0x38, 0x31, 0x0a, 0x0a, 0x27, 0x52, 0x29, 0x52, 0x36,
313 	0x5a, 0x37, 0x59, 0x39, 0x59, 0x3b, 0x57, 0x4a, 0x39, 0x4b, 0x3a, 0x3a,
314 	0x5c, 0x38, 0x5c, 0x08, 0x02, 0x35, 0x45, 0x2e, 0x51, 0x08, 0x02, 0x3b,
315 	0x48, 0x34, 0x54, 0x08, 0x02, 0x3b, 0x53, 0x2d, 0x4b, 0x08, 0x02, 0x2f,
316 	0x47, 0x3d, 0x4e, 0x0a, 0x08, 0x31, 0x42, 0x30, 0x40, 0x3f, 0x22, 0x52,
317 	0x29, 0x52, 0x2a, 0x43, 0x48, 0x42, 0x49, 0x3f, 0x49, 0x0a, 0x0a, 0x31,
318 	0x42, 0x30, 0x40, 0x3f, 0x22, 0x52, 0x29, 0x52, 0x2a, 0x43, 0x48, 0x42,
319 	0x49, 0x3f, 0x49, 0x50, 0x29, 0x3f, 0x23, 0x0a, 0x04, 0x31, 0x42, 0x3f,
320 	0x23, 0x50, 0x29, 0x3f, 0x49, 0x0a, 0x04, 0x48, 0x28, 0x47, 0x29, 0x44,
321 	0x28, 0x45, 0x27, 0x0a, 0x04, 0x43, 0x26, 0x44, 0x25, 0x4b, 0x28, 0x4a,
322 	0x29, 0x0a, 0x04, 0x4c, 0x2c, 0x44, 0x3c, 0x38, 0x36, 0x3f, 0x27, 0x0a,
323 	0x04, 0x39, 0x3e, 0x3c, 0x40, 0x3d, 0x3e, 0xbd, 0x62, 0xbe, 0x44, 0x08,
324 	0x02, 0x37, 0x40, 0x3c, 0x43, 0x08, 0x02, 0x36, 0x42, 0x3a, 0x39, 0x08,
325 	0x02, 0x35, 0x3d, 0x37, 0x3e, 0x08, 0x02, 0x3b, 0x45, 0x40, 0x3c, 0x08,
326 	0x02, 0x40, 0x43, 0x3d, 0x41, 0x08, 0x03, 0x38, 0x36, 0x3f, 0x27, 0x4c,
327 	0x2c, 0x0f, 0x0a, 0x01, 0x01, 0x00, 0x10, 0x01, 0x15, 0x84, 0x00, 0x04,
328 	0x0a, 0x00, 0x01, 0x01, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x02, 0x04,
329 	0x0a, 0x00, 0x01, 0x01, 0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x02, 0x04,
330 	0x0a, 0x03, 0x01, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x03, 0x00, 0x0a, 0x08,
331 	0x04, 0x04, 0x05, 0x06, 0x07, 0x18, 0x15, 0xff, 0x01, 0x17, 0x81, 0x00,
332 	0x04, 0x0a, 0x00, 0x01, 0x08, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x02,
333 	0x04, 0x0a, 0x00, 0x01, 0x08, 0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x02,
334 	0x04, 0x0a, 0x05, 0x01, 0x09, 0x00, 0x0a, 0x02, 0x01, 0x0a, 0x00, 0x0a,
335 	0x00, 0x01, 0x0b, 0x08, 0x15, 0xff, 0x0a, 0x06, 0x01, 0x0c, 0x08, 0x15,
336 	0xff, 0x0a, 0x07, 0x01, 0x0d, 0x00, 0x0a, 0x06, 0x01, 0x0e, 0x08, 0x15,
337 	0xff, 0x0a, 0x08, 0x06, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x14, 0x18, 0x15,
338 	0xff, 0x01, 0x17, 0x81, 0x00, 0x04
339 };
340 
341 device_icon kKeyIconData = { (int32)sizeof(kDeviceIcon), (void *)kDeviceIcon };
342 device_icon kCDIconData = { (int32)sizeof(kCDIcon), (void *)kCDIcon };
343 device_icon kMSIconData = { (int32)sizeof(kMemoryStickIcon),
344 	(void *)kMemoryStickIcon };
345 device_icon kSDIconData = { (int32)sizeof(kSDIcon), (void *)kSDIcon };
346 device_icon kMobileIconData = { (int32)sizeof(kMobileIcon),
347 	(void *)kMobileIcon };
348 
349 
350 struct {
351 	const char *vendor;
352 	const char *product;
353 	device_icon *icon;
354 	const char *name;
355 } kIconMatches[] = {
356 	// matches for Hama USB 2.0 Card Reader 35 in 1
357 	// vendor: "Transcend Information, Inc."
358 	// product: "63-in-1 Multi-Card Reader/Writer" ver. 0100
359 	// which report things like "Generic " "USB  CF Reader  "
360 //	{ NULL, " CF Reader", &kCFIconData, "devices/drive-removable-media-flash" },
361 	{ NULL, " SD Reader", &kSDIconData, "devices/drive-removable-media-flash" },
362 	{ NULL, " MS Reader", &kMSIconData, "devices/drive-removable-media-flash" },
363 //	{ NULL, " SM Reader", &kSMIconData, "devices/drive-removable-media-flash" },
364 	// match for my Kazam mobile phone
365 	// stupid thing says "MEDIATEK" " FLASH DISK     " even for internal memory
366 	{ "MEDIATEK", NULL, &kMobileIconData, "devices/drive-removable-media-flash" },
367 	{ NULL, NULL, NULL, NULL }
368 };
369 
370 
371 //
372 //#pragma mark - Forward Declarations
373 //
374 
375 
376 static void	usb_disk_callback(void *cookie, status_t status, void *data,
377 				size_t actualLength);
378 
379 status_t	usb_disk_mass_storage_reset(disk_device *device);
380 uint8		usb_disk_get_max_lun(disk_device *device);
381 void		usb_disk_reset_recovery(disk_device *device);
382 status_t	usb_disk_transfer_data(disk_device *device, bool directionIn,
383 				void *data, size_t dataLength);
384 status_t	usb_disk_receive_csw(disk_device *device,
385 				usb_massbulk_command_status_wrapper *status);
386 status_t	usb_disk_operation(device_lun *lun, uint8 operation,
387 				uint8 opLength, uint32 logicalBlockAddress,
388 				uint16 transferLength, void *data, size_t *dataLength,
389 				bool directionIn, err_act *action = NULL);
390 
391 status_t	usb_disk_request_sense(device_lun *lun, err_act *action);
392 status_t	usb_disk_mode_sense(device_lun *lun);
393 status_t	usb_disk_test_unit_ready(device_lun *lun, err_act *action = NULL);
394 status_t	usb_disk_inquiry(device_lun *lun);
395 status_t	usb_disk_reset_capacity(device_lun *lun);
396 status_t	usb_disk_update_capacity(device_lun *lun);
397 status_t	usb_disk_synchronize(device_lun *lun, bool force);
398 
399 
400 //
401 //#pragma mark - Device Allocation Helper Functions
402 //
403 
404 
405 void
406 usb_disk_free_device_and_luns(disk_device *device)
407 {
408 	mutex_lock(&device->lock);
409 	mutex_destroy(&device->lock);
410 	delete_sem(device->notify);
411 	for (uint8 i = 0; i < device->lun_count; i++)
412 		free(device->luns[i]);
413 	free(device->luns);
414 	free(device);
415 }
416 
417 
418 //
419 //#pragma mark - Bulk-only Mass Storage Functions
420 //
421 
422 
423 status_t
424 usb_disk_mass_storage_reset(disk_device *device)
425 {
426 	return gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_OUT
427 		| USB_REQTYPE_CLASS, USB_MASSBULK_REQUEST_MASS_STORAGE_RESET, 0x0000,
428 		device->interface, 0, NULL, NULL);
429 }
430 
431 
432 uint8
433 usb_disk_get_max_lun(disk_device *device)
434 {
435 	uint8 result = 0;
436 	size_t actualLength = 0;
437 
438 	// devices that do not support multiple LUNs may stall this request
439 	if (gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_IN
440 		| USB_REQTYPE_CLASS, USB_MASSBULK_REQUEST_GET_MAX_LUN, 0x0000,
441 		device->interface, 1, &result, &actualLength) != B_OK
442 			|| actualLength != 1) {
443 		return 0;
444 	}
445 
446 	if (result > MAX_LOGICAL_UNIT_NUMBER) {
447 		// invalid max lun
448 		return 0;
449 	}
450 
451 	return result;
452 }
453 
454 
455 void
456 usb_disk_reset_recovery(disk_device *device)
457 {
458 	usb_disk_mass_storage_reset(device);
459 	gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
460 	gUSBModule->clear_feature(device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
461 }
462 
463 
464 status_t
465 usb_disk_transfer_data(disk_device *device, bool directionIn, void *data,
466 	size_t dataLength)
467 {
468 	status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in
469 		: device->bulk_out, data, dataLength, usb_disk_callback, device);
470 	if (result != B_OK) {
471 		TRACE_ALWAYS("failed to queue data transfer: %s\n", strerror(result));
472 		return result;
473 	}
474 
475 	do {
476 		result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT,
477 			10 * 1000 * 1000);
478 		if (result == B_TIMED_OUT) {
479 			// Cancel the transfer and collect the sem that should now be
480 			// released through the callback on cancel. Handling of device
481 			// reset is done in usb_disk_operation() when it detects that
482 			// the transfer failed.
483 			gUSBModule->cancel_queued_transfers(directionIn ? device->bulk_in
484 				: device->bulk_out);
485 			acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0);
486 		}
487 	} while (result == B_INTERRUPTED);
488 
489 	if (result != B_OK) {
490 		TRACE_ALWAYS("acquire_sem failed while waiting for data transfer: %s\n",
491 			strerror(result));
492 		return result;
493 	}
494 
495 	return B_OK;
496 }
497 
498 
499 status_t
500 usb_disk_receive_csw(disk_device *device,
501 	usb_massbulk_command_status_wrapper *status)
502 {
503 	status_t result = usb_disk_transfer_data(device, true, status,
504 		sizeof(usb_massbulk_command_status_wrapper));
505 	if (result != B_OK)
506 		return result;
507 
508 	if (device->status != B_OK
509 		|| device->actual_length
510 			!= sizeof(usb_massbulk_command_status_wrapper)) {
511 		// receiving the command status wrapper failed
512 		return B_ERROR;
513 	}
514 
515 	return B_OK;
516 }
517 
518 
519 status_t
520 usb_disk_operation(device_lun *lun, uint8 operation, uint8 opLength,
521 	uint32 logicalBlockAddress, uint16 transferLength, void *data,
522 	size_t *dataLength, bool directionIn, err_act *_action)
523 {
524 	TRACE("operation: lun: %u; op: %u; oplen: %u; lba: %" B_PRIu32
525 		"; tlen: %u; data: %p; dlen: %p (%lu); in: %c\n",
526 		lun->logical_unit_number, operation, opLength, logicalBlockAddress,
527 		transferLength, data, dataLength, dataLength ? *dataLength : 0,
528 		directionIn ? 'y' : 'n');
529 
530 	disk_device *device = lun->device;
531 	usb_massbulk_command_block_wrapper command;
532 	command.signature = USB_MASSBULK_CBW_SIGNATURE;
533 	command.tag = device->current_tag++;
534 	command.data_transfer_length = (dataLength != NULL ? *dataLength : 0);
535 	command.flags = (directionIn ? USB_MASSBULK_CBW_DATA_INPUT
536 		: USB_MASSBULK_CBW_DATA_OUTPUT);
537 	command.lun = lun->logical_unit_number;
538 	command.command_block_length
539 		= device->is_atapi ? ATAPI_COMMAND_LENGTH : opLength;
540 	memset(command.command_block, 0, sizeof(command.command_block));
541 
542 	switch (opLength) {
543 		case 6:
544 		{
545 			scsi_command_6 *commandBlock
546 				= (scsi_command_6 *)command.command_block;
547 			commandBlock->operation = operation;
548 			commandBlock->lun = lun->logical_unit_number << 5;
549 			commandBlock->allocation_length = (uint8)transferLength;
550 			if (operation == SCSI_MODE_SENSE_6) {
551 				// we hijack the lba argument to transport the desired page
552 				commandBlock->reserved[1] = (uint8)logicalBlockAddress;
553 			}
554 			break;
555 		}
556 
557 		case 10:
558 		{
559 			scsi_command_10 *commandBlock
560 				= (scsi_command_10 *)command.command_block;
561 			commandBlock->operation = operation;
562 			commandBlock->lun_flags = lun->logical_unit_number << 5;
563 			commandBlock->logical_block_address = htonl(logicalBlockAddress);
564 			commandBlock->transfer_length = htons(transferLength);
565 			break;
566 		}
567 
568 		default:
569 			TRACE_ALWAYS("unsupported operation length %d\n", opLength);
570 			return B_BAD_VALUE;
571 	}
572 
573 	status_t result = usb_disk_transfer_data(device, false, &command,
574 		sizeof(usb_massbulk_command_block_wrapper));
575 	if (result != B_OK)
576 		return result;
577 
578 	if (device->status != B_OK ||
579 		device->actual_length != sizeof(usb_massbulk_command_block_wrapper)) {
580 		// sending the command block wrapper failed
581 		TRACE_ALWAYS("sending the command block wrapper failed: %s\n",
582 			strerror(device->status));
583 		usb_disk_reset_recovery(device);
584 		return B_ERROR;
585 	}
586 
587 	size_t transferedData = 0;
588 	if (data != NULL && dataLength != NULL && *dataLength > 0) {
589 		// we have data to transfer in a data stage
590 		result = usb_disk_transfer_data(device, directionIn, data,
591 			*dataLength);
592 		if (result != B_OK)
593 			return result;
594 
595 		transferedData = device->actual_length;
596 		if (device->status != B_OK || transferedData != *dataLength) {
597 			// sending or receiving of the data failed
598 			if (device->status == B_DEV_STALLED) {
599 				TRACE("stall while transfering data\n");
600 				gUSBModule->clear_feature(directionIn ? device->bulk_in
601 					: device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
602 			} else {
603 				TRACE_ALWAYS("sending or receiving of the data failed: %s\n",
604 					strerror(device->status));
605 				usb_disk_reset_recovery(device);
606 				return B_ERROR;
607 			}
608 		}
609 	}
610 
611 	usb_massbulk_command_status_wrapper status;
612 	result =  usb_disk_receive_csw(device, &status);
613 	if (result != B_OK) {
614 		// in case of a stall or error clear the stall and try again
615 		gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
616 		result = usb_disk_receive_csw(device, &status);
617 	}
618 
619 	if (result != B_OK) {
620 		TRACE_ALWAYS("receiving the command status wrapper failed: %s\n",
621 			strerror(result));
622 		usb_disk_reset_recovery(device);
623 		return result;
624 	}
625 
626 	if (status.signature != USB_MASSBULK_CSW_SIGNATURE
627 		|| status.tag != command.tag) {
628 		// the command status wrapper is not valid
629 		TRACE_ALWAYS("command status wrapper is not valid: %#" B_PRIx32 "\n",
630 			status.signature);
631 		usb_disk_reset_recovery(device);
632 		return B_ERROR;
633 	}
634 
635 	switch (status.status) {
636 		case USB_MASSBULK_CSW_STATUS_COMMAND_PASSED:
637 		case USB_MASSBULK_CSW_STATUS_COMMAND_FAILED:
638 		{
639 			// The residue from "status.data_residue" is not maintained
640 			// correctly by some devices, so calculate it instead.
641 			uint32 residue = command.data_transfer_length - transferedData;
642 
643 			if (dataLength != NULL) {
644 				*dataLength -= residue;
645 				if (transferedData < *dataLength) {
646 					TRACE_ALWAYS("less data transfered than indicated: %"
647 						B_PRIuSIZE " vs. %" B_PRIuSIZE "\n", transferedData,
648 						*dataLength);
649 					*dataLength = transferedData;
650 				}
651 			}
652 
653 			if (status.status == USB_MASSBULK_CSW_STATUS_COMMAND_PASSED) {
654 				// the operation is complete and has succeeded
655 				return B_OK;
656 			} else {
657 				if (operation == SCSI_REQUEST_SENSE_6)
658 					return B_ERROR;
659 
660 				// the operation is complete but has failed at the SCSI level
661 				if (operation != SCSI_TEST_UNIT_READY_6) {
662 					TRACE_ALWAYS("operation %#" B_PRIx8
663 						" failed at the SCSI level\n", operation);
664 				}
665 
666 				result = usb_disk_request_sense(lun, _action);
667 				return result == B_OK ? B_ERROR : result;
668 			}
669 		}
670 
671 		case USB_MASSBULK_CSW_STATUS_PHASE_ERROR:
672 		{
673 			// a protocol or device error occured
674 			TRACE_ALWAYS("phase error in operation %#" B_PRIx8 "\n", operation);
675 			usb_disk_reset_recovery(device);
676 			return B_ERROR;
677 		}
678 
679 		default:
680 		{
681 			// command status wrapper is not meaningful
682 			TRACE_ALWAYS("command status wrapper has invalid status\n");
683 			usb_disk_reset_recovery(device);
684 			return B_ERROR;
685 		}
686 	}
687 }
688 
689 
690 //
691 //#pragma mark - Helper/Convenience Functions
692 //
693 
694 
695 status_t
696 usb_disk_request_sense(device_lun *lun, err_act *_action)
697 {
698 	size_t dataLength = sizeof(scsi_request_sense_6_parameter);
699 	scsi_request_sense_6_parameter parameter;
700 	status_t result = usb_disk_operation(lun, SCSI_REQUEST_SENSE_6, 6, 0,
701 		dataLength, &parameter, &dataLength, true);
702 	if (result != B_OK) {
703 		TRACE_ALWAYS("getting request sense data failed: %s\n",
704 			strerror(result));
705 		return result;
706 	}
707 
708 	const char *label = NULL;
709 	err_act action = err_act_fail;
710 	status_t status = B_ERROR;
711 	scsi_get_sense_asc_info((parameter.additional_sense_code << 8)
712 		| parameter.additional_sense_code_qualifier, &label, &action,
713 		&status);
714 
715 	if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY
716 		&& parameter.sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) {
717 		TRACE_ALWAYS("request_sense: key: 0x%02x; asc: 0x%02x; ascq: "
718 			"0x%02x; %s\n", parameter.sense_key, parameter.additional_sense_code,
719 			parameter.additional_sense_code_qualifier,
720 			label ? label : "(unknown)");
721 	}
722 
723 	if ((parameter.additional_sense_code == 0
724 			&& parameter.additional_sense_code_qualifier == 0)
725 		|| label == NULL) {
726 		scsi_get_sense_key_info(parameter.sense_key, &label, &action, &status);
727 	}
728 
729 	if (status == B_DEV_MEDIA_CHANGED) {
730 		lun->media_changed = true;
731 		lun->media_present = true;
732 	} else if (parameter.sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION
733 		&& status != B_DEV_NO_MEDIA) {
734 		lun->media_present = true;
735 	} else if (status == B_DEV_NOT_READY) {
736 		lun->media_present = false;
737 		usb_disk_reset_capacity(lun);
738 	}
739 
740 	if (_action != NULL)
741 		*_action = action;
742 
743 	return status;
744 }
745 
746 
747 status_t
748 usb_disk_mode_sense(device_lun *lun)
749 {
750 	size_t dataLength = sizeof(scsi_mode_sense_6_parameter);
751 	scsi_mode_sense_6_parameter parameter;
752 	status_t result = usb_disk_operation(lun, SCSI_MODE_SENSE_6, 6,
753 		SCSI_MODE_PAGE_DEVICE_CONFIGURATION, dataLength, &parameter,
754 		&dataLength, true);
755 	if (result != B_OK) {
756 		TRACE_ALWAYS("getting mode sense data failed: %s\n", strerror(result));
757 		return result;
758 	}
759 
760 	lun->write_protected
761 		= (parameter.device_specific & SCSI_DEVICE_SPECIFIC_WRITE_PROTECT)
762 			!= 0;
763 	TRACE_ALWAYS("write protected: %s\n", lun->write_protected ? "yes" : "no");
764 	return B_OK;
765 }
766 
767 
768 status_t
769 usb_disk_test_unit_ready(device_lun *lun, err_act *_action)
770 {
771 	// if unsupported we assume the unit is fixed and therefore always ok
772 	if (!lun->device->tur_supported)
773 		return B_OK;
774 
775 	status_t result;
776 	if (lun->device->is_atapi) {
777 		result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 1,
778 			NULL, NULL, false, _action);
779 	} else {
780 		result = usb_disk_operation(lun, SCSI_TEST_UNIT_READY_6, 6, 0, 0,
781 			NULL, NULL, true, _action);
782 	}
783 
784 	if (result == B_DEV_INVALID_IOCTL) {
785 		lun->device->tur_supported = false;
786 		return B_OK;
787 	}
788 
789 	return result;
790 }
791 
792 
793 status_t
794 usb_disk_inquiry(device_lun *lun)
795 {
796 	size_t dataLength = sizeof(scsi_inquiry_6_parameter);
797 	scsi_inquiry_6_parameter parameter;
798 	status_t result = B_ERROR;
799 	err_act action = err_act_ok;
800 	for (uint32 tries = 0; tries < 3; tries++) {
801 		result = usb_disk_operation(lun, SCSI_INQUIRY_6, 6, 0, dataLength,
802 			&parameter, &dataLength, true, &action);
803 		if (result == B_OK || (action != err_act_retry
804 				&& action != err_act_many_retries)) {
805 			break;
806 		}
807 	}
808 	if (result != B_OK) {
809 		TRACE_ALWAYS("getting inquiry data failed: %s\n", strerror(result));
810 		lun->device_type = B_DISK;
811 		lun->removable = true;
812 		return result;
813 	}
814 
815 	TRACE("peripherial_device_type  0x%02x\n",
816 		parameter.peripherial_device_type);
817 	TRACE("peripherial_qualifier    0x%02x\n",
818 		parameter.peripherial_qualifier);
819 	TRACE("removable_medium         %s\n",
820 		parameter.removable_medium ? "yes" : "no");
821 	TRACE("version                  0x%02x\n", parameter.version);
822 	TRACE("response_data_format     0x%02x\n", parameter.response_data_format);
823 	TRACE_ALWAYS("vendor_identification    \"%.8s\"\n",
824 		parameter.vendor_identification);
825 	TRACE_ALWAYS("product_identification   \"%.16s\"\n",
826 		parameter.product_identification);
827 	TRACE_ALWAYS("product_revision_level   \"%.4s\"\n",
828 		parameter.product_revision_level);
829 
830 	memcpy(lun->vendor_name, parameter.vendor_identification,
831 		MIN(sizeof(lun->vendor_name), sizeof(parameter.vendor_identification)));
832 	memcpy(lun->product_name, parameter.product_identification,
833 		MIN(sizeof(lun->product_name),
834 			sizeof(parameter.product_identification)));
835 	memcpy(lun->product_revision, parameter.product_revision_level,
836 		MIN(sizeof(lun->product_revision),
837 			sizeof(parameter.product_revision_level)));
838 
839 	lun->device_type = parameter.peripherial_device_type; /* 1:1 mapping */
840 	lun->removable = (parameter.removable_medium == 1);
841 	return B_OK;
842 }
843 
844 
845 status_t
846 usb_disk_reset_capacity(device_lun *lun)
847 {
848 	lun->block_size = 512;
849 	lun->block_count = 0;
850 	return B_OK;
851 }
852 
853 
854 status_t
855 usb_disk_update_capacity(device_lun *lun)
856 {
857 	size_t dataLength = sizeof(scsi_read_capacity_10_parameter);
858 	scsi_read_capacity_10_parameter parameter;
859 	status_t result = B_ERROR;
860 	err_act action = err_act_ok;
861 
862 	// Retry reading the capacity up to three times. The first try might only
863 	// yield a unit attention telling us that the device or media status
864 	// changed, which is more or less expected if it is the first operation
865 	// on the device or the device only clears the unit atention for capacity
866 	// reads.
867 	for (int32 i = 0; i < 3; i++) {
868 		result = usb_disk_operation(lun, SCSI_READ_CAPACITY_10, 10, 0, 0,
869 			&parameter, &dataLength, true, &action);
870 		if (result == B_OK || (action != err_act_retry
871 				&& action != err_act_many_retries)) {
872 			break;
873 		}
874 	}
875 
876 	if (result != B_OK) {
877 		TRACE_ALWAYS("failed to update capacity: %s\n", strerror(result));
878 		lun->media_present = false;
879 		lun->media_changed = false;
880 		usb_disk_reset_capacity(lun);
881 		return result;
882 	}
883 
884 	lun->media_present = true;
885 	lun->media_changed = false;
886 	lun->block_size = ntohl(parameter.logical_block_length);
887 	lun->block_count = ntohl(parameter.last_logical_block_address) + 1;
888 	return B_OK;
889 }
890 
891 
892 status_t
893 usb_disk_synchronize(device_lun *lun, bool force)
894 {
895 	if (lun->device->sync_support == 0) {
896 		// this device reported an illegal request when syncing or repeatedly
897 		// returned an other error, it apparently does not support syncing...
898 		return B_UNSUPPORTED;
899 	}
900 
901 	if (!lun->should_sync && !force)
902 		return B_OK;
903 
904 	status_t result = usb_disk_operation(lun, SCSI_SYNCHRONIZE_CACHE_10, 10,
905 		0, 0, NULL, NULL, false);
906 
907 	if (result == B_OK) {
908 		lun->device->sync_support = SYNC_SUPPORT_RELOAD;
909 		lun->should_sync = false;
910 		return B_OK;
911 	}
912 
913 	if (result == B_DEV_INVALID_IOCTL)
914 		lun->device->sync_support = 0;
915 	else
916 		lun->device->sync_support--;
917 
918 	return result;
919 }
920 
921 
922 //
923 //#pragma mark - Device Attach/Detach Notifications and Callback
924 //
925 
926 
927 static void
928 usb_disk_callback(void *cookie, status_t status, void *data,
929 	size_t actualLength)
930 {
931 	//TRACE("callback()\n");
932 	disk_device *device = (disk_device *)cookie;
933 	device->status = status;
934 	device->actual_length = actualLength;
935 	release_sem(device->notify);
936 }
937 
938 
939 static status_t
940 usb_disk_device_added(usb_device newDevice, void **cookie)
941 {
942 	TRACE("device_added(0x%08" B_PRIx32 ")\n", newDevice);
943 	disk_device *device = (disk_device *)malloc(sizeof(disk_device));
944 	device->device = newDevice;
945 	device->removed = false;
946 	device->open_count = 0;
947 	device->interface = 0xff;
948 	device->current_tag = 0;
949 	device->sync_support = SYNC_SUPPORT_RELOAD;
950 	device->tur_supported = true;
951 	device->is_atapi = false;
952 	device->luns = NULL;
953 
954 	// scan through the interfaces to find our bulk-only data interface
955 	const usb_configuration_info *configuration
956 		= gUSBModule->get_configuration(newDevice);
957 	if (configuration == NULL) {
958 		free(device);
959 		return B_ERROR;
960 	}
961 
962 	for (size_t i = 0; i < configuration->interface_count; i++) {
963 		usb_interface_info *interface = configuration->interface[i].active;
964 		if (interface == NULL)
965 			continue;
966 
967 		if (interface->descr->interface_class == USB_MASS_STORAGE_DEVICE_CLASS
968 			&& (interface->descr->interface_subclass == 0x06 /* SCSI */
969 				|| interface->descr->interface_subclass == 0x02 /* ATAPI */
970 				|| interface->descr->interface_subclass == 0x05 /* ATAPI */)
971 			&& interface->descr->interface_protocol == 0x50 /* bulk-only */) {
972 
973 			bool hasIn = false;
974 			bool hasOut = false;
975 			for (size_t j = 0; j < interface->endpoint_count; j++) {
976 				usb_endpoint_info *endpoint = &interface->endpoint[j];
977 				if (endpoint == NULL
978 					|| endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK)
979 					continue;
980 
981 				if (!hasIn && (endpoint->descr->endpoint_address
982 					& USB_ENDPOINT_ADDR_DIR_IN) != 0) {
983 					device->bulk_in = endpoint->handle;
984 					hasIn = true;
985 				} else if (!hasOut && (endpoint->descr->endpoint_address
986 					& USB_ENDPOINT_ADDR_DIR_IN) == 0) {
987 					device->bulk_out = endpoint->handle;
988 					hasOut = true;
989 				}
990 
991 				if (hasIn && hasOut)
992 					break;
993 			}
994 
995 			if (!(hasIn && hasOut))
996 				continue;
997 
998 			device->interface = interface->descr->interface_number;
999 			device->is_atapi = interface->descr->interface_subclass != 0x06;
1000 			break;
1001 		}
1002 	}
1003 
1004 	if (device->interface == 0xff) {
1005 		TRACE_ALWAYS("no valid bulk-only interface found\n");
1006 		free(device);
1007 		return B_ERROR;
1008 	}
1009 
1010 	mutex_init(&device->lock, "usb_disk device lock");
1011 
1012 	device->notify = create_sem(0, "usb_disk callback notify");
1013 	if (device->notify < B_OK) {
1014 		mutex_destroy(&device->lock);
1015 		status_t result = device->notify;
1016 		free(device);
1017 		return result;
1018 	}
1019 
1020 	device->lun_count = usb_disk_get_max_lun(device) + 1;
1021 	device->luns = (device_lun **)malloc(device->lun_count
1022 		* sizeof(device_lun *));
1023 	for (uint8 i = 0; i < device->lun_count; i++)
1024 		device->luns[i] = NULL;
1025 
1026 	status_t result = B_OK;
1027 
1028 	TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count);
1029 	for (uint8 i = 0; i < device->lun_count; i++) {
1030 		// create the individual luns present on this device
1031 		device_lun *lun = (device_lun *)malloc(sizeof(device_lun));
1032 		if (lun == NULL) {
1033 			result = B_NO_MEMORY;
1034 			break;
1035 		}
1036 
1037 		device->luns[i] = lun;
1038 		lun->device = device;
1039 		lun->logical_unit_number = i;
1040 		lun->should_sync = false;
1041 		lun->media_present = true;
1042 		lun->media_changed = true;
1043 
1044 		memset(lun->vendor_name, 0, sizeof(lun->vendor_name));
1045 		memset(lun->product_name, 0, sizeof(lun->product_name));
1046 		memset(lun->product_revision, 0, sizeof(lun->product_revision));
1047 
1048 		usb_disk_reset_capacity(lun);
1049 
1050 		// initialize this lun
1051 		result = usb_disk_inquiry(lun);
1052 		err_act action = err_act_ok;
1053 		for (uint32 tries = 0; tries < 8; tries++) {
1054 			TRACE("usb lun %" B_PRIu8 " inquiry attempt %" B_PRIu32 " begin\n",
1055 				i, tries);
1056 			status_t ready = usb_disk_test_unit_ready(lun, &action);
1057 			if (ready == B_OK || ready == B_DEV_NO_MEDIA
1058 				|| ready == B_DEV_MEDIA_CHANGED) {
1059 				if (lun->device_type == B_CD)
1060 					lun->write_protected = true;
1061 				// TODO: check for write protection; disabled since some
1062 				// devices lock up when getting the mode sense
1063 				else if (/*usb_disk_mode_sense(lun) != B_OK*/true)
1064 					lun->write_protected = false;
1065 
1066 				TRACE("usb lun %" B_PRIu8 " ready. write protected = %c%s\n", i,
1067 					lun->write_protected ? 'y' : 'n',
1068 					ready == B_DEV_NO_MEDIA ? " (no media inserted)" : "");
1069 
1070 				break;
1071 			}
1072 			TRACE("usb lun %" B_PRIu8 " inquiry attempt %" B_PRIu32 " failed\n",
1073 				i, tries);
1074 			if (action != err_act_retry && action != err_act_many_retries)
1075 				break;
1076 			bigtime_t snoozeTime = 1000000 * tries;
1077 			TRACE("snoozing %" B_PRIu64 " microseconds for usb lun\n",
1078 				snoozeTime);
1079 			snooze(snoozeTime);
1080 		}
1081 
1082 		if (result != B_OK)
1083 			break;
1084 	}
1085 
1086 	if (result != B_OK) {
1087 		TRACE_ALWAYS("failed to initialize logical units: %s\n",
1088 			strerror(result));
1089 		usb_disk_free_device_and_luns(device);
1090 		return result;
1091 	}
1092 
1093 	mutex_lock(&gDeviceListLock);
1094 	device->device_number = 0;
1095 	disk_device *other = gDeviceList;
1096 	while (other != NULL) {
1097 		if (other->device_number >= device->device_number)
1098 			device->device_number = other->device_number + 1;
1099 
1100 		other = (disk_device *)other->link;
1101 	}
1102 
1103 	device->link = (void *)gDeviceList;
1104 	gDeviceList = device;
1105 	gLunCount += device->lun_count;
1106 	for (uint8 i = 0; i < device->lun_count; i++)
1107 		sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i);
1108 	mutex_unlock(&gDeviceListLock);
1109 
1110 	TRACE("new device: 0x%p\n", device);
1111 	*cookie = (void *)device;
1112 	return B_OK;
1113 }
1114 
1115 
1116 static status_t
1117 usb_disk_device_removed(void *cookie)
1118 {
1119 	TRACE("device_removed(0x%p)\n", cookie);
1120 	disk_device *device = (disk_device *)cookie;
1121 
1122 	mutex_lock(&gDeviceListLock);
1123 	if (gDeviceList == device) {
1124 		gDeviceList = (disk_device *)device->link;
1125 	} else {
1126 		disk_device *element = gDeviceList;
1127 		while (element) {
1128 			if (element->link == device) {
1129 				element->link = device->link;
1130 				break;
1131 			}
1132 
1133 			element = (disk_device *)element->link;
1134 		}
1135 	}
1136 	gLunCount -= device->lun_count;
1137 	gDeviceCount--;
1138 
1139 	device->removed = true;
1140 	gUSBModule->cancel_queued_transfers(device->bulk_in);
1141 	gUSBModule->cancel_queued_transfers(device->bulk_out);
1142 	if (device->open_count == 0)
1143 		usb_disk_free_device_and_luns(device);
1144 
1145 	mutex_unlock(&gDeviceListLock);
1146 	return B_OK;
1147 }
1148 
1149 
1150 //
1151 //#pragma mark - Partial Buffer Functions
1152 //
1153 
1154 
1155 static bool
1156 usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length,
1157 	uint32 &blockPosition, uint16 &blockCount)
1158 {
1159 	blockPosition = (uint32)(position / lun->block_size);
1160 	if ((off_t)blockPosition * lun->block_size != position)
1161 		return true;
1162 
1163 	blockCount = (uint16)(length / lun->block_size);
1164 	if ((size_t)blockCount * lun->block_size != length)
1165 		return true;
1166 
1167 	return false;
1168 }
1169 
1170 
1171 static status_t
1172 usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount,
1173 	void *buffer, size_t *length)
1174 {
1175 	status_t result = usb_disk_operation(lun, SCSI_READ_10, 10, blockPosition,
1176 		blockCount, buffer, length, true);
1177 	return result;
1178 }
1179 
1180 
1181 static status_t
1182 usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount,
1183 	void *buffer, size_t *length)
1184 {
1185 	status_t result = usb_disk_operation(lun, SCSI_WRITE_10, 10, blockPosition,
1186 		blockCount, buffer, length, false);
1187 	if (result == B_OK)
1188 		lun->should_sync = true;
1189 	return result;
1190 }
1191 
1192 
1193 static status_t
1194 usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length,
1195 	void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition,
1196 	uint16 &blockCount)
1197 {
1198 	blockPosition = (uint32)(position / lun->block_size);
1199 	blockCount = (uint16)((uint32)((position + length + lun->block_size - 1)
1200 		/ lun->block_size) - blockPosition);
1201 	size_t blockLength = blockCount * lun->block_size;
1202 	blockBuffer = malloc(blockLength);
1203 	if (blockBuffer == NULL) {
1204 		TRACE_ALWAYS("no memory to allocate partial buffer\n");
1205 		return B_NO_MEMORY;
1206 	}
1207 
1208 	status_t result = usb_disk_block_read(lun, blockPosition, blockCount,
1209 		blockBuffer, &blockLength);
1210 	if (result != B_OK) {
1211 		TRACE_ALWAYS("block read failed when filling partial buffer: %s\n",
1212 			strerror(result));
1213 		free(blockBuffer);
1214 		return result;
1215 	}
1216 
1217 	off_t offset = position - (off_t)blockPosition * lun->block_size;
1218 	partialBuffer = (uint8 *)blockBuffer + offset;
1219 	return B_OK;
1220 }
1221 
1222 
1223 //
1224 //#pragma mark - Driver Hooks
1225 //
1226 
1227 
1228 static status_t
1229 usb_disk_open(const char *name, uint32 flags, void **cookie)
1230 {
1231 	TRACE("open(%s)\n", name);
1232 	if (strncmp(name, DEVICE_NAME_BASE, strlen(DEVICE_NAME_BASE)) != 0)
1233 		return B_NAME_NOT_FOUND;
1234 
1235 	int32 lastPart = 0;
1236 	size_t nameLength = strlen(name);
1237 	for (int32 i = nameLength - 1; i >= 0; i--) {
1238 		if (name[i] == '/') {
1239 			lastPart = i;
1240 			break;
1241 		}
1242 	}
1243 
1244 	char rawName[nameLength + 4];
1245 	strncpy(rawName, name, lastPart + 1);
1246 	rawName[lastPart + 1] = 0;
1247 	strcat(rawName, "raw");
1248 	TRACE("opening raw device %s for %s\n", rawName, name);
1249 
1250 	mutex_lock(&gDeviceListLock);
1251 	disk_device *device = gDeviceList;
1252 	while (device) {
1253 		for (uint8 i = 0; i < device->lun_count; i++) {
1254 			device_lun *lun = device->luns[i];
1255 			if (strncmp(rawName, lun->name, 32) == 0) {
1256 				// found the matching device/lun
1257 				if (device->removed) {
1258 					mutex_unlock(&gDeviceListLock);
1259 					return B_ERROR;
1260 				}
1261 
1262 				device->open_count++;
1263 				*cookie = lun;
1264 				mutex_unlock(&gDeviceListLock);
1265 				return B_OK;
1266 			}
1267 		}
1268 
1269 		device = (disk_device *)device->link;
1270 	}
1271 
1272 	mutex_unlock(&gDeviceListLock);
1273 	return B_NAME_NOT_FOUND;
1274 }
1275 
1276 
1277 static status_t
1278 usb_disk_close(void *cookie)
1279 {
1280 	TRACE("close()\n");
1281 	device_lun *lun = (device_lun *)cookie;
1282 	disk_device *device = lun->device;
1283 
1284 	mutex_lock(&device->lock);
1285 	if (!device->removed)
1286 		usb_disk_synchronize(lun, false);
1287 	mutex_unlock(&device->lock);
1288 
1289 	return B_OK;
1290 }
1291 
1292 
1293 static status_t
1294 usb_disk_free(void *cookie)
1295 {
1296 	TRACE("free()\n");
1297 	mutex_lock(&gDeviceListLock);
1298 
1299 	device_lun *lun = (device_lun *)cookie;
1300 	disk_device *device = lun->device;
1301 	device->open_count--;
1302 	if (device->open_count == 0 && device->removed) {
1303 		// we can simply free the device here as it has been removed from
1304 		// the device list in the device removed notification hook
1305 		usb_disk_free_device_and_luns(device);
1306 	}
1307 
1308 	mutex_unlock(&gDeviceListLock);
1309 	return B_OK;
1310 }
1311 
1312 
1313 static inline void
1314 normalize_name(char *name, size_t nameLength)
1315 {
1316 	bool wasSpace = false;
1317 	size_t insertIndex = 0;
1318 	for (size_t i = 0; i < nameLength; i++) {
1319 		bool isSpace = name[i] == ' ';
1320 		if (isSpace && wasSpace)
1321 			continue;
1322 
1323 		name[insertIndex++] = name[i];
1324 		wasSpace = isSpace;
1325 	}
1326 
1327 	if (insertIndex > 0 && name[insertIndex - 1] == ' ')
1328 		insertIndex--;
1329 
1330 	name[insertIndex] = 0;
1331 }
1332 
1333 
1334 static status_t
1335 usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
1336 {
1337 	device_lun *lun = (device_lun *)cookie;
1338 	disk_device *device = lun->device;
1339 	mutex_lock(&device->lock);
1340 	if (device->removed) {
1341 		mutex_unlock(&device->lock);
1342 		return B_DEV_NOT_READY;
1343 	}
1344 
1345 	status_t result = B_DEV_INVALID_IOCTL;
1346 	switch (op) {
1347 		case B_GET_MEDIA_STATUS:
1348 		{
1349 			err_act action = err_act_ok;
1350 			for (uint32 tries = 0; tries < 3; tries++) {
1351 				status_t ready = usb_disk_test_unit_ready(lun, &action);
1352 				if (ready == B_OK || ready == B_DEV_NO_MEDIA
1353 					|| (action != err_act_retry
1354 						&& action != err_act_many_retries)) {
1355 					*(status_t *)buffer = ready;
1356 					break;
1357 				}
1358 				snooze(500000);
1359 			}
1360 			TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n",
1361 				*(status_t *)buffer);
1362 			result = B_OK;
1363 			break;
1364 		}
1365 
1366 		case B_GET_GEOMETRY:
1367 		{
1368 			if (lun->media_changed) {
1369 				result = usb_disk_update_capacity(lun);
1370 				if (result != B_OK)
1371 					break;
1372 			}
1373 
1374 			device_geometry geometry;
1375 			devfs_compute_geometry_size(&geometry, lun->block_count,
1376 				lun->block_size);
1377 
1378 			geometry.device_type = lun->device_type;
1379 			geometry.removable = lun->removable;
1380 			geometry.read_only = lun->write_protected;
1381 			geometry.write_once = lun->device_type == B_WORM;
1382 			TRACE("B_GET_GEOMETRY: %" B_PRId32 " sectors at %" B_PRId32
1383 				" bytes per sector\n", geometry.cylinder_count,
1384 				geometry.bytes_per_sector);
1385 			result = user_memcpy(buffer, &geometry, sizeof(device_geometry));
1386 			break;
1387 		}
1388 
1389 		case B_FLUSH_DRIVE_CACHE:
1390 			TRACE("B_FLUSH_DRIVE_CACHE\n");
1391 			result = usb_disk_synchronize(lun, true);
1392 			break;
1393 
1394 		case B_EJECT_DEVICE:
1395 			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 2,
1396 				NULL, NULL, false);
1397 			break;
1398 
1399 		case B_LOAD_MEDIA:
1400 			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 3,
1401 				NULL, NULL, false);
1402 			break;
1403 
1404 #if HAIKU_TARGET_PLATFORM_HAIKU
1405 		case B_GET_ICON:
1406 			// We don't support this legacy ioctl anymore, but the two other
1407 			// icon ioctls below instead.
1408 			break;
1409 
1410 		case B_GET_ICON_NAME:
1411 		{
1412 			const char *iconName = "devices/drive-removable-media-usb";
1413 			char vendor[sizeof(lun->vendor_name)+1];
1414 			char product[sizeof(lun->product_name)+1];
1415 
1416 			switch (lun->device_type) {
1417 				case B_CD:
1418 				case B_OPTICAL:
1419 					iconName = "devices/drive-optical";
1420 					break;
1421 				case B_TAPE:	// TODO
1422 				default:
1423 					snprintf(vendor, sizeof(vendor), "%.8s",
1424 						lun->vendor_name);
1425 					snprintf(product, sizeof(product), "%.16s",
1426 						lun->product_name);
1427 					for (int i = 0; kIconMatches[i].icon; i++) {
1428 						if (kIconMatches[i].vendor != NULL
1429 							&& strstr(vendor, kIconMatches[i].vendor) == NULL)
1430 							continue;
1431 						if (kIconMatches[i].product != NULL
1432 							&& strstr(product, kIconMatches[i].product) == NULL)
1433 							continue;
1434 						iconName = kIconMatches[i].name;
1435 					}
1436 					break;
1437 			}
1438 			result = user_strlcpy((char *)buffer, iconName,
1439 				B_FILE_NAME_LENGTH);
1440 			break;
1441 		}
1442 
1443 		case B_GET_VECTOR_ICON:
1444 		{
1445 			device_icon *icon = &kKeyIconData;
1446 			char vendor[sizeof(lun->vendor_name)+1];
1447 			char product[sizeof(lun->product_name)+1];
1448 
1449 			if (length != sizeof(device_icon)) {
1450 				result = B_BAD_VALUE;
1451 				break;
1452 			}
1453 
1454 			switch (lun->device_type) {
1455 				case B_CD:
1456 				case B_OPTICAL:
1457 					icon = &kCDIconData;
1458 					break;
1459 				case B_TAPE:	// TODO
1460 				default:
1461 					snprintf(vendor, sizeof(vendor), "%.8s",
1462 						lun->vendor_name);
1463 					snprintf(product, sizeof(product), "%.16s",
1464 						lun->product_name);
1465 					for (int i = 0; kIconMatches[i].icon; i++) {
1466 						if (kIconMatches[i].vendor != NULL
1467 							&& strstr(vendor, kIconMatches[i].vendor) == NULL)
1468 							continue;
1469 						if (kIconMatches[i].product != NULL
1470 							&& strstr(product, kIconMatches[i].product) == NULL)
1471 							continue;
1472 						icon = kIconMatches[i].icon;
1473 					}
1474 					break;
1475 			}
1476 
1477 			device_icon iconData;
1478 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) {
1479 				result = B_BAD_ADDRESS;
1480 				break;
1481 			}
1482 
1483 			if (iconData.icon_size >= icon->icon_size) {
1484 				if (user_memcpy(iconData.icon_data, icon->icon_data,
1485 						(size_t)icon->icon_size) != B_OK) {
1486 					result = B_BAD_ADDRESS;
1487 					break;
1488 				}
1489 			}
1490 
1491 			iconData.icon_size = icon->icon_size;
1492 			result = user_memcpy(buffer, &iconData, sizeof(device_icon));
1493 			break;
1494 		}
1495 
1496 		case B_GET_DEVICE_NAME:
1497 		{
1498 			size_t nameLength = sizeof(lun->vendor_name)
1499 				+ sizeof(lun->product_name) + sizeof(lun->product_revision) + 3;
1500 
1501 			char name[nameLength];
1502 			snprintf(name, nameLength, "%.8s %.16s %.4s", lun->vendor_name,
1503 				lun->product_name, lun->product_revision);
1504 
1505 			normalize_name(name, nameLength);
1506 
1507 			result = user_strlcpy((char *)buffer, name, length);
1508 			if (result > 0)
1509 				result = B_OK;
1510 
1511 			TRACE_ALWAYS("got device name \"%s\": %s\n", name,
1512 				strerror(result));
1513 			break;
1514 		}
1515 #endif
1516 
1517 		default:
1518 			TRACE_ALWAYS("unhandled ioctl %" B_PRId32 "\n", op);
1519 			break;
1520 	}
1521 
1522 	mutex_unlock(&device->lock);
1523 	return result;
1524 }
1525 
1526 
1527 static status_t
1528 usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length)
1529 {
1530 	if (buffer == NULL || length == NULL)
1531 		return B_BAD_VALUE;
1532 
1533 	TRACE("read(%" B_PRIdOFF ", %ld)\n", position, *length);
1534 	device_lun *lun = (device_lun *)cookie;
1535 	disk_device *device = lun->device;
1536 	mutex_lock(&device->lock);
1537 	if (device->removed) {
1538 		*length = 0;
1539 		mutex_unlock(&device->lock);
1540 		return B_DEV_NOT_READY;
1541 	}
1542 
1543 	status_t result = B_ERROR;
1544 	uint32 blockPosition = 0;
1545 	uint16 blockCount = 0;
1546 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position, *length,
1547 		blockPosition, blockCount);
1548 	if (needsPartial) {
1549 		void *partialBuffer = NULL;
1550 		void *blockBuffer = NULL;
1551 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
1552 			partialBuffer, blockBuffer, blockPosition, blockCount);
1553 		if (result == B_OK) {
1554 			memcpy(buffer, partialBuffer, *length);
1555 			free(blockBuffer);
1556 		}
1557 	} else {
1558 		result = usb_disk_block_read(lun, blockPosition, blockCount, buffer,
1559 			length);
1560 	}
1561 
1562 	mutex_unlock(&device->lock);
1563 	if (result == B_OK) {
1564 		TRACE("read successful with %ld bytes\n", *length);
1565 		return B_OK;
1566 	}
1567 
1568 	*length = 0;
1569 	TRACE_ALWAYS("read failed: %s\n", strerror(result));
1570 	return result;
1571 }
1572 
1573 
1574 static status_t
1575 usb_disk_write(void *cookie, off_t position, const void *buffer,
1576 	size_t *length)
1577 {
1578 	if (buffer == NULL || length == NULL)
1579 		return B_BAD_VALUE;
1580 
1581 	TRACE("write(%" B_PRIdOFF", %ld)\n", position, *length);
1582 	device_lun *lun = (device_lun *)cookie;
1583 	disk_device *device = lun->device;
1584 	mutex_lock(&device->lock);
1585 	if (device->removed) {
1586 		*length = 0;
1587 		mutex_unlock(&device->lock);
1588 		return B_DEV_NOT_READY;
1589 	}
1590 
1591 	status_t result = B_ERROR;
1592 	uint32 blockPosition = 0;
1593 	uint16 blockCount = 0;
1594 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position,
1595 		*length, blockPosition, blockCount);
1596 	if (needsPartial) {
1597 		void *partialBuffer = NULL;
1598 		void *blockBuffer = NULL;
1599 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
1600 			partialBuffer, blockBuffer, blockPosition, blockCount);
1601 		if (result == B_OK) {
1602 			memcpy(partialBuffer, buffer, *length);
1603 			size_t blockLength = blockCount * lun->block_size;
1604 			result = usb_disk_block_write(lun, blockPosition, blockCount,
1605 				blockBuffer, &blockLength);
1606 			free(blockBuffer);
1607 		}
1608 	} else {
1609 		result = usb_disk_block_write(lun, blockPosition, blockCount,
1610 			(void *)buffer, length);
1611 	}
1612 
1613 	mutex_unlock(&device->lock);
1614 	if (result == B_OK) {
1615 		TRACE("write successful with %ld bytes\n", *length);
1616 		return B_OK;
1617 	}
1618 
1619 	*length = 0;
1620 	TRACE_ALWAYS("write failed: %s\n", strerror(result));
1621 	return result;
1622 }
1623 
1624 
1625 //
1626 //#pragma mark - Driver Entry Points
1627 //
1628 
1629 
1630 status_t
1631 init_hardware()
1632 {
1633 	TRACE("init_hardware()\n");
1634 	return B_OK;
1635 }
1636 
1637 
1638 status_t
1639 init_driver()
1640 {
1641 	TRACE("init_driver()\n");
1642 	static usb_notify_hooks notifyHooks = {
1643 		&usb_disk_device_added,
1644 		&usb_disk_device_removed
1645 	};
1646 
1647 	static usb_support_descriptor supportedDevices[] = {
1648 		{ 0x08 /* mass storage */, 0x06 /* SCSI */, 0x50 /* bulk */, 0, 0 },
1649 		{ 0x08 /* mass storage */, 0x02 /* ATAPI */, 0x50 /* bulk */, 0, 0 },
1650 		{ 0x08 /* mass storage */, 0x05 /* ATAPI */, 0x50 /* bulk */, 0, 0 }
1651 	};
1652 
1653 	gDeviceList = NULL;
1654 	gDeviceCount = 0;
1655 	gLunCount = 0;
1656 	mutex_init(&gDeviceListLock, "usb_disk device list lock");
1657 
1658 	TRACE("trying module %s\n", B_USB_MODULE_NAME);
1659 	status_t result = get_module(B_USB_MODULE_NAME,
1660 		(module_info **)&gUSBModule);
1661 	if (result < B_OK) {
1662 		TRACE_ALWAYS("getting module failed: %s\n", strerror(result));
1663 		mutex_destroy(&gDeviceListLock);
1664 		return result;
1665 	}
1666 
1667 	gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 3, NULL);
1668 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
1669 	return B_OK;
1670 }
1671 
1672 
1673 void
1674 uninit_driver()
1675 {
1676 	TRACE("uninit_driver()\n");
1677 	gUSBModule->uninstall_notify(DRIVER_NAME);
1678 	mutex_lock(&gDeviceListLock);
1679 
1680 	if (gDeviceNames) {
1681 		for (int32 i = 0; gDeviceNames[i]; i++)
1682 			free(gDeviceNames[i]);
1683 		free(gDeviceNames);
1684 		gDeviceNames = NULL;
1685 	}
1686 
1687 	mutex_destroy(&gDeviceListLock);
1688 	put_module(B_USB_MODULE_NAME);
1689 }
1690 
1691 
1692 const char **
1693 publish_devices()
1694 {
1695 	TRACE("publish_devices()\n");
1696 	if (gDeviceNames) {
1697 		for (int32 i = 0; gDeviceNames[i]; i++)
1698 			free(gDeviceNames[i]);
1699 		free(gDeviceNames);
1700 		gDeviceNames = NULL;
1701 	}
1702 
1703 	gDeviceNames = (char **)malloc(sizeof(char *) * (gLunCount + 1));
1704 	if (gDeviceNames == NULL)
1705 		return NULL;
1706 
1707 	int32 index = 0;
1708 	mutex_lock(&gDeviceListLock);
1709 	disk_device *device = gDeviceList;
1710 	while (device) {
1711 		for (uint8 i = 0; i < device->lun_count; i++)
1712 			gDeviceNames[index++] = strdup(device->luns[i]->name);
1713 
1714 		device = (disk_device *)device->link;
1715 	}
1716 
1717 	gDeviceNames[index++] = NULL;
1718 	mutex_unlock(&gDeviceListLock);
1719 	return (const char **)gDeviceNames;
1720 }
1721 
1722 
1723 device_hooks *
1724 find_device(const char *name)
1725 {
1726 	TRACE("find_device()\n");
1727 	static device_hooks hooks = {
1728 		&usb_disk_open,
1729 		&usb_disk_close,
1730 		&usb_disk_free,
1731 		&usb_disk_ioctl,
1732 		&usb_disk_read,
1733 		&usb_disk_write,
1734 		NULL,
1735 		NULL,
1736 		NULL,
1737 		NULL
1738 	};
1739 
1740 	return &hooks;
1741 }
1742