OLD | NEW |
1 /** | 1 /** |
2 * \file mtp-probe.c | 2 * \file mtp-probe.c |
3 * Program to probe newly connected device interfaces from | 3 * Program to probe newly connected device interfaces from |
4 * userspace to determine if they are MTP devices, used for | 4 * userspace to determine if they are MTP devices, used for |
5 * udev rules. | 5 * udev rules. |
6 * | 6 * |
7 * Invoke the program from udev to check it for MTP signatures, | 7 * Invoke the program from udev to check it for MTP signatures, |
8 * e.g. | 8 * e.g. |
9 * ATTR{bDeviceClass}=="ff", | 9 * ATTR{bDeviceClass}=="ff", |
10 * PROGRAM="<path>/mtp-probe /sys$env{DEVPATH} $attr{busnum} $attr{devnum}", | 10 * PROGRAM="<path>/mtp-probe /sys$env{DEVPATH} $attr{busnum} $attr{devnum}", |
(...skipping 27 matching lines...) Expand all Loading... |
38 */ | 38 */ |
39 #ifndef __linux__ | 39 #ifndef __linux__ |
40 #error "This program should only be compiled for Linux!" | 40 #error "This program should only be compiled for Linux!" |
41 #endif | 41 #endif |
42 | 42 |
43 #include <unistd.h> | 43 #include <unistd.h> |
44 #include <stdlib.h> | 44 #include <stdlib.h> |
45 #include <stdio.h> | 45 #include <stdio.h> |
46 #include <string.h> | 46 #include <string.h> |
47 #include <syslog.h> | 47 #include <syslog.h> |
48 #include <sys/types.h> | |
49 #include <sys/stat.h> | |
50 #include <dirent.h> | |
51 #include <libmtp.h> | 48 #include <libmtp.h> |
52 #include <regex.h> | |
53 #include <fcntl.h> | |
54 | |
55 enum ep_type { | |
56 OTHER_EP, | |
57 BULK_OUT_EP, | |
58 BULK_IN_EP, | |
59 INTERRUPT_IN_EP, | |
60 INTERRUPT_OUT_EP, | |
61 }; | |
62 | |
63 static enum ep_type get_ep_type(char *path) | |
64 { | |
65 char pbuf[FILENAME_MAX]; | |
66 int len = strlen(path); | |
67 int fd; | |
68 char buf[128]; | |
69 int bread; | |
70 int is_out = 0; | |
71 int is_in = 0; | |
72 int is_bulk = 0; | |
73 int is_interrupt = 0; | |
74 int i; | |
75 | |
76 strcpy(pbuf, path); | |
77 pbuf[len++] = '/'; | |
78 | |
79 /* Check the type */ | |
80 strncpy(pbuf + len, "type", FILENAME_MAX - len); | |
81 pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ | |
82 | |
83 fd = open(pbuf, O_RDONLY); | |
84 if (fd < 0) | |
85 return OTHER_EP; | |
86 bread = read(fd, buf, sizeof(buf)); | |
87 close(fd); | |
88 if (bread < 2) | |
89 return OTHER_EP; | |
90 | |
91 for (i = 0; i < bread; i++) | |
92 if(buf[i] == 0x0d || buf[i] == 0x0a) | |
93 buf[i] = '\0'; | |
94 | |
95 if (!strcmp(buf, "Bulk")) | |
96 is_bulk = 1; | |
97 if (!strcmp(buf, "Interrupt")) | |
98 is_interrupt = 1; | |
99 | |
100 /* Check the direction */ | |
101 strncpy(pbuf + len, "direction", FILENAME_MAX - len); | |
102 pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ | |
103 | |
104 fd = open(pbuf, O_RDONLY); | |
105 if (fd < 0) | |
106 return OTHER_EP; | |
107 bread = read(fd, buf, sizeof(buf)); | |
108 close(fd); | |
109 if (bread < 2) | |
110 return OTHER_EP; | |
111 | |
112 for (i = 0; i < bread; i++) | |
113 if(buf[i] == 0x0d || buf[i] == 0x0a) | |
114 buf[i] = '\0'; | |
115 | |
116 if (!strcmp(buf, "in")) | |
117 is_in = 1; | |
118 if (!strcmp(buf, "out")) | |
119 is_out = 1; | |
120 | |
121 if (is_bulk && is_in) | |
122 return BULK_IN_EP; | |
123 if (is_bulk && is_out) | |
124 return BULK_OUT_EP; | |
125 if (is_interrupt && is_in) | |
126 return INTERRUPT_IN_EP; | |
127 if (is_interrupt && is_out) | |
128 return INTERRUPT_OUT_EP; | |
129 | |
130 return OTHER_EP; | |
131 } | |
132 | |
133 static int has_3_ep(char *path) | |
134 { | |
135 char pbuf[FILENAME_MAX]; | |
136 int len = strlen(path); | |
137 int fd; | |
138 char buf[128]; | |
139 int bread; | |
140 | |
141 strcpy(pbuf, path); | |
142 pbuf[len++] = '/'; | |
143 strncpy(pbuf + len, "bNumEndpoints", FILENAME_MAX - len); | |
144 pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ | |
145 | |
146 fd = open(pbuf, O_RDONLY); | |
147 if (fd < 0) | |
148 return -1; | |
149 /* Read all contents to buffer */ | |
150 bread = read(fd, buf, sizeof(buf)); | |
151 close(fd); | |
152 if (bread < 2) | |
153 return 0; | |
154 | |
155 /* 0x30, 0x33 = "03", maybe we should parse it? */ | |
156 if (buf[0] == 0x30 && buf[1] == 0x33) | |
157 return 1; | |
158 | |
159 return 0; | |
160 } | |
161 | |
162 static int check_interface(char *sysfspath) | |
163 { | |
164 char dirbuf[FILENAME_MAX]; | |
165 int len = strlen(sysfspath); | |
166 DIR *dir; | |
167 struct dirent *dent; | |
168 regex_t r; | |
169 int ret; | |
170 int bulk_out_ep_found = 0; | |
171 int bulk_in_ep_found = 0; | |
172 int interrupt_in_ep_found = 0; | |
173 | |
174 ret = has_3_ep(sysfspath); | |
175 if (ret <= 0) | |
176 return ret; | |
177 | |
178 /* Yes it has three endpoints ... look even closer! */ | |
179 dir = opendir(sysfspath); | |
180 if (!dir) | |
181 return -1; | |
182 | |
183 strcpy(dirbuf, sysfspath); | |
184 dirbuf[len++] = '/'; | |
185 | |
186 /* Check for dirs that identify endpoints */ | |
187 ret = regcomp(&r, "^ep_[0-9a-f]+$", REG_EXTENDED | REG_NOSUB); | |
188 if (ret) { | |
189 closedir(dir); | |
190 return -1; | |
191 } | |
192 | |
193 while ((dent = readdir(dir))) { | |
194 struct stat st; | |
195 | |
196 /* No need to check those beginning with a period */ | |
197 if (dent->d_name[0] == '.') | |
198 continue; | |
199 | |
200 strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len); | |
201 dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ | |
202 ret = lstat(dirbuf, &st); | |
203 if (ret) | |
204 continue; | |
205 if (S_ISDIR(st.st_mode) && !regexec(&r, dent->d_name, 0, 0, 0)) { | |
206 enum ep_type ept; | |
207 | |
208 ept = get_ep_type(dirbuf); | |
209 if (ept == BULK_OUT_EP) | |
210 bulk_out_ep_found = 1; | |
211 else if (ept == BULK_IN_EP) | |
212 bulk_in_ep_found = 1; | |
213 else if (ept == INTERRUPT_IN_EP) | |
214 interrupt_in_ep_found = 1; | |
215 } | |
216 } | |
217 | |
218 regfree(&r); | |
219 closedir(dir); | |
220 | |
221 /* | |
222 * If this is fulfilled the interface is an MTP candidate | |
223 */ | |
224 if (bulk_out_ep_found && | |
225 bulk_in_ep_found && | |
226 interrupt_in_ep_found) { | |
227 return 1; | |
228 } | |
229 | |
230 return 0; | |
231 } | |
232 | |
233 static int check_sysfs(char *sysfspath) | |
234 { | |
235 char dirbuf[FILENAME_MAX]; | |
236 int len = strlen(sysfspath); | |
237 DIR *dir; | |
238 struct dirent *dent; | |
239 regex_t r; | |
240 int ret; | |
241 int look_closer = 0; | |
242 | |
243 dir = opendir(sysfspath); | |
244 if (!dir) | |
245 return -1; | |
246 | |
247 strcpy(dirbuf, sysfspath); | |
248 dirbuf[len++] = '/'; | |
249 | |
250 /* Check for dirs that identify interfaces */ | |
251 ret = regcomp(&r, "^[0-9]+-[0-9]+(\\.[0-9])*\\:[0-9]+\\.[0-9]+$", REG_EXTENDED
| REG_NOSUB); | |
252 if (ret) { | |
253 closedir(dir); | |
254 return -1; | |
255 } | |
256 | |
257 while ((dent = readdir(dir))) { | |
258 struct stat st; | |
259 int ret; | |
260 | |
261 /* No need to check those beginning with a period */ | |
262 if (dent->d_name[0] == '.') | |
263 continue; | |
264 | |
265 strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len); | |
266 dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ | |
267 ret = lstat(dirbuf, &st); | |
268 if (ret) | |
269 continue; | |
270 | |
271 /* Look closer at dirs that may be interfaces */ | |
272 if (S_ISDIR(st.st_mode)) { | |
273 if (!regexec(&r, dent->d_name, 0, 0, 0)) | |
274 if (check_interface(dirbuf) > 0) | |
275 /* potential MTP interface! */ | |
276 look_closer = 1; | |
277 } | |
278 } | |
279 | |
280 regfree(&r); | |
281 closedir(dir); | |
282 return look_closer; | |
283 } | |
284 | 49 |
285 int main (int argc, char **argv) | 50 int main (int argc, char **argv) |
286 { | 51 { |
287 char *fname; | 52 char *fname; |
288 int busno; | 53 int busno; |
289 int devno; | 54 int devno; |
290 int ret; | 55 int ret; |
291 | 56 |
292 if (argc < 4) { | 57 if (argc < 4) { |
293 syslog(LOG_INFO, "need device path, busnumber, device number as argument\n")
; | 58 syslog(LOG_INFO, "need device path, busnumber, device number as argument\n")
; |
294 printf("0"); | 59 printf("0"); |
295 exit(0); | 60 exit(0); |
296 } | 61 } |
297 | 62 |
298 fname = argv[1]; | 63 fname = argv[1]; |
299 busno = atoi(argv[2]); | 64 busno = atoi(argv[2]); |
300 devno = atoi(argv[3]); | 65 devno = atoi(argv[3]); |
301 | 66 |
302 syslog(LOG_INFO, "checking bus %d, device %d: \"%s\"\n", busno, devno, fname); | 67 syslog(LOG_INFO, "checking bus %d, device %d: \"%s\"\n", busno, devno, fname); |
303 | 68 |
304 ret = check_sysfs(fname); | 69 ret = LIBMTP_Check_Specific_Device(busno, devno); |
305 /* | |
306 * This means that regular directory check either agrees that this may be a | |
307 * MTP device, or that it doesn't know (failed). In that case, kick the deeper | |
308 * check inside LIBMTP. | |
309 */ | |
310 if (ret != 0) | |
311 ret = LIBMTP_Check_Specific_Device(busno, devno); | |
312 if (ret) { | 70 if (ret) { |
313 syslog(LOG_INFO, "bus: %d, device: %d was an MTP device\n", busno, devno); | 71 syslog(LOG_INFO, "bus: %d, device: %d was an MTP device\n", busno, devno); |
314 printf("1"); | 72 printf("1"); |
315 } else { | 73 } else { |
316 syslog(LOG_INFO, "bus: %d, device: %d was not an MTP device\n", busno, devno
); | 74 syslog(LOG_INFO, "bus: %d, device: %d was not an MTP device\n", busno, devno
); |
317 printf("0"); | 75 printf("0"); |
318 } | 76 } |
319 | 77 |
320 exit(0); | 78 exit(0); |
321 } | 79 } |
OLD | NEW |