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> |
48 #include <libmtp.h> | 51 #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 } |
49 | 284 |
50 int main (int argc, char **argv) | 285 int main (int argc, char **argv) |
51 { | 286 { |
52 char *fname; | 287 char *fname; |
53 int busno; | 288 int busno; |
54 int devno; | 289 int devno; |
55 int ret; | 290 int ret; |
56 | 291 |
57 if (argc < 4) { | 292 if (argc < 4) { |
58 syslog(LOG_INFO, "need device path, busnumber, device number as argument\n")
; | 293 syslog(LOG_INFO, "need device path, busnumber, device number as argument\n")
; |
59 printf("0"); | 294 printf("0"); |
60 exit(0); | 295 exit(0); |
61 } | 296 } |
62 | 297 |
63 fname = argv[1]; | 298 fname = argv[1]; |
64 busno = atoi(argv[2]); | 299 busno = atoi(argv[2]); |
65 devno = atoi(argv[3]); | 300 devno = atoi(argv[3]); |
66 | 301 |
67 syslog(LOG_INFO, "checking bus %d, device %d: \"%s\"\n", busno, devno, fname); | 302 syslog(LOG_INFO, "checking bus %d, device %d: \"%s\"\n", busno, devno, fname); |
68 | 303 |
69 ret = LIBMTP_Check_Specific_Device(busno, devno); | 304 ret = check_sysfs(fname); |
| 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); |
70 if (ret) { | 312 if (ret) { |
71 syslog(LOG_INFO, "bus: %d, device: %d was an MTP device\n", busno, devno); | 313 syslog(LOG_INFO, "bus: %d, device: %d was an MTP device\n", busno, devno); |
72 printf("1"); | 314 printf("1"); |
73 } else { | 315 } else { |
74 syslog(LOG_INFO, "bus: %d, device: %d was not an MTP device\n", busno, devno
); | 316 syslog(LOG_INFO, "bus: %d, device: %d was not an MTP device\n", busno, devno
); |
75 printf("0"); | 317 printf("0"); |
76 } | 318 } |
77 | 319 |
78 exit(0); | 320 exit(0); |
79 } | 321 } |
OLD | NEW |