3 * Program to probe newly connected device interfaces from
4 * userspace to determine if they are MTP devices, used for
7 * Invoke the program from udev to check it for MTP signatures,
9 * ATTR{bDeviceClass}=="ff",
10 * PROGRAM="<path>/mtp-probe /sys$env{DEVPATH} $attr{busnum} $attr{devnum}",
11 * RESULT=="1", ENV{ID_MTP_DEVICE}="1", ENV{ID_MEDIA_PLAYER}="1",
12 * SYMLINK+="libmtp-%k", MODE="666"
14 * Is you issue this before testing your /var/log/messages
15 * will be more verbose:
17 * udevadm control --log-priority=debug
19 * Exits with status code 1 if the device is an MTP device,
22 * Copyright (C) 2011-2012 Linus Walleij <triad@df.lth.se>
24 * This library is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU Lesser General Public
26 * License as published by the Free Software Foundation; either
27 * version 2 of the License, or (at your option) any later version.
29 * This library is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32 * Lesser General Public License for more details.
34 * You should have received a copy of the GNU Lesser General Public
35 * License along with this library; if not, write to the
36 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
37 * Boston, MA 02111-1307, USA.
40 #error "This program should only be compiled for Linux!"
48 #include <sys/types.h>
63 static enum ep_type get_ep_type(char *path)
65 char pbuf[FILENAME_MAX];
66 int len = strlen(path);
80 strncpy(pbuf + len, "type", FILENAME_MAX - len);
81 pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */
83 fd = open(pbuf, O_RDONLY);
86 bread = read(fd, buf, sizeof(buf));
91 for (i = 0; i < bread; i++)
92 if(buf[i] == 0x0d || buf[i] == 0x0a)
95 if (!strcmp(buf, "Bulk"))
97 if (!strcmp(buf, "Interrupt"))
100 /* Check the direction */
101 strncpy(pbuf + len, "direction", FILENAME_MAX - len);
102 pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */
104 fd = open(pbuf, O_RDONLY);
107 bread = read(fd, buf, sizeof(buf));
112 for (i = 0; i < bread; i++)
113 if(buf[i] == 0x0d || buf[i] == 0x0a)
116 if (!strcmp(buf, "in"))
118 if (!strcmp(buf, "out"))
121 if (is_bulk && is_in)
123 if (is_bulk && is_out)
125 if (is_interrupt && is_in)
126 return INTERRUPT_IN_EP;
127 if (is_interrupt && is_out)
128 return INTERRUPT_OUT_EP;
133 static int has_3_ep(char *path)
135 char pbuf[FILENAME_MAX];
136 int len = strlen(path);
143 strncpy(pbuf + len, "bNumEndpoints", FILENAME_MAX - len);
144 pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */
146 fd = open(pbuf, O_RDONLY);
149 /* Read all contents to buffer */
150 bread = read(fd, buf, sizeof(buf));
155 /* 0x30, 0x33 = "03", maybe we should parse it? */
156 if (buf[0] == 0x30 && buf[1] == 0x33)
162 static int check_interface(char *sysfspath)
164 char dirbuf[FILENAME_MAX];
165 int len = strlen(sysfspath);
170 int bulk_out_ep_found = 0;
171 int bulk_in_ep_found = 0;
172 int interrupt_in_ep_found = 0;
174 ret = has_3_ep(sysfspath);
178 /* Yes it has three endpoints ... look even closer! */
179 dir = opendir(sysfspath);
183 strcpy(dirbuf, sysfspath);
186 /* Check for dirs that identify endpoints */
187 ret = regcomp(&r, "^ep_[0-9a-f]+$", REG_EXTENDED | REG_NOSUB);
193 while ((dent = readdir(dir))) {
196 /* No need to check those beginning with a period */
197 if (dent->d_name[0] == '.')
200 strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len);
201 dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */
202 ret = lstat(dirbuf, &st);
205 if (S_ISDIR(st.st_mode) && !regexec(&r, dent->d_name, 0, 0, 0)) {
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;
222 * If this is fulfilled the interface is an MTP candidate
224 if (bulk_out_ep_found &&
226 interrupt_in_ep_found) {
233 static int check_sysfs(char *sysfspath)
235 char dirbuf[FILENAME_MAX];
236 int len = strlen(sysfspath);
243 dir = opendir(sysfspath);
247 strcpy(dirbuf, sysfspath);
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);
257 while ((dent = readdir(dir))) {
261 /* No need to check those beginning with a period */
262 if (dent->d_name[0] == '.')
265 strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len);
266 dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */
267 ret = lstat(dirbuf, &st);
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! */
285 int main (int argc, char **argv)
293 syslog(LOG_INFO, "need device path, busnumber, device number as argument\n");
299 busno = atoi(argv[2]);
300 devno = atoi(argv[3]);
302 syslog(LOG_INFO, "checking bus %d, device %d: \"%s\"\n", busno, devno, fname);
304 ret = check_sysfs(fname);
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.
311 ret = LIBMTP_Check_Specific_Device(busno, devno);
313 syslog(LOG_INFO, "bus: %d, device: %d was an MTP device\n", busno, devno);
316 syslog(LOG_INFO, "bus: %d, device: %d was not an MTP device\n", busno, devno);