1 /**
2 * \file mtp-hotplug.c
3 * Program to create hotplug scripts.
4 *
5 * Copyright (C) 2005-2012 Linus Walleij <triad@df.lth.se>
6 * Copyright (C) 2006-2008 Marcus Meissner <marcus@jet.franken.de>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23 #include <libmtp.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28
usage(void)29 static void usage(void)
30 {
31 fprintf(stderr, "usage: hotplug [-u -H -i -a\"ACTION\"] -p\"DIR\" -g\"GROUP\" -m\"MODE\"\n");
32 fprintf(stderr, " -w: use hwdb syntax\n");
33 fprintf(stderr, " -u: use udev syntax\n");
34 fprintf(stderr, " -o: use old udev syntax\n");
35 fprintf(stderr, " -H: use hal syntax\n");
36 fprintf(stderr, " -i: use usb.ids simple list syntax\n");
37 fprintf(stderr, " -a\"ACTION\": perform udev action ACTION on attachment\n");
38 fprintf(stderr, " -p\"DIR\": directory where mtp-probe will be installed\n");
39 fprintf(stderr, " -g\"GROUP\": file group for device nodes\n");
40 fprintf(stderr, " -m\"MODE\": file mode for device nodes\n");
41 exit(1);
42 }
43
44 enum style {
45 style_usbmap,
46 style_udev,
47 style_udev_old,
48 style_hal,
49 style_usbids,
50 style_hwdb
51 };
52
main(int argc,char ** argv)53 int main (int argc, char **argv)
54 {
55 LIBMTP_device_entry_t *entries;
56 int numentries;
57 int i;
58 int ret;
59 enum style style = style_usbmap;
60 int opt;
61 extern int optind;
62 extern char *optarg;
63 char *udev_action = NULL;
64 /*
65 * You could tag on MODE="0666" here to enfore writeable
66 * device nodes, use the command line argument for that.
67 * Current udev default rules will make any device tagged
68 * with ENV{ID_MEDIA_PLAYER}=1 writable for the console
69 * user.
70 */
71 char default_udev_action[] = "SYMLINK+=\"libmtp-%k\", ENV{ID_MTP_DEVICE}=\"1\", ENV{ID_MEDIA_PLAYER}=\"1\"";
72 char *action; // To hold the action actually used.
73 uint16_t last_vendor = 0x0000U;
74 char mtp_probe_dir[256];
75 char *udev_group= NULL;
76 char *udev_mode = NULL;
77
78 while ( (opt = getopt(argc, argv, "wuoiHa:p:g:m:")) != -1 ) {
79 switch (opt) {
80 case 'a':
81 udev_action = strdup(optarg);
82 break;
83 case 'u':
84 style = style_udev;
85 break;
86 case 'o':
87 style = style_udev_old;
88 break;
89 case 'H':
90 style = style_hal;
91 break;
92 case 'i':
93 style = style_usbids;
94 break;
95 case 'w':
96 style = style_hwdb;
97 break;
98 case 'p':
99 strncpy(mtp_probe_dir,optarg,sizeof(mtp_probe_dir));
100 mtp_probe_dir[sizeof(mtp_probe_dir)-1] = '\0';
101 if (strlen(mtp_probe_dir) <= 1) {
102 printf("Supply some sane mtp-probe dir\n");
103 exit(1);
104 }
105 /* Make sure the dir ends with '/' */
106 if (mtp_probe_dir[strlen(mtp_probe_dir)-1] != '/') {
107 int index = strlen(mtp_probe_dir);
108 if (index >= (sizeof(mtp_probe_dir)-1)) {
109 exit(1);
110 }
111 mtp_probe_dir[index] = '/';
112 mtp_probe_dir[index+1] = '\0';
113 }
114 /* Don't add the standard udev path... */
115 if (!strcmp(mtp_probe_dir, "/lib/udev/")) {
116 mtp_probe_dir[0] = '\0';
117 }
118 break;
119 case 'g':
120 udev_group = strdup(optarg);
121 break;
122 case 'm':
123 udev_mode = strdup(optarg);
124 break;
125 default:
126 usage();
127 }
128 }
129
130 if (udev_action != NULL) {
131 action = udev_action;
132 } else {
133 action = default_udev_action;
134 }
135
136 LIBMTP_Init();
137 ret = LIBMTP_Get_Supported_Devices_List(&entries, &numentries);
138 if (ret == 0) {
139 switch (style) {
140 case style_udev:
141 printf("# UDEV-style hotplug map for libmtp\n");
142 printf("# Put this file in /etc/udev/rules.d\n\n");
143 printf("ACTION!=\"add\", GOTO=\"libmtp_rules_end\"\n");
144 printf("ENV{MAJOR}!=\"?*\", GOTO=\"libmtp_rules_end\"\n");
145 printf("SUBSYSTEM==\"usb\", GOTO=\"libmtp_usb_rules\"\n"
146 "GOTO=\"libmtp_rules_end\"\n\n"
147 "LABEL=\"libmtp_usb_rules\"\n\n");
148 printf("# Some sensitive devices we surely don\'t wanna probe\n");
149 printf("# Color instruments\n");
150 printf("ATTR{idVendor}==\"0670\", GOTO=\"libmtp_rules_end\"\n");
151 printf("ATTR{idVendor}==\"0765\", GOTO=\"libmtp_rules_end\"\n");
152 printf("ATTR{idVendor}==\"085c\", GOTO=\"libmtp_rules_end\"\n");
153 printf("ATTR{idVendor}==\"0971\", GOTO=\"libmtp_rules_end\"\n");
154 printf("# Canon scanners that look like MTP devices (PID 0x22nn)\n");
155 printf("ATTR{idVendor}==\"04a9\", ATTR{idProduct}==\"22*\", GOTO=\"libmtp_rules_end\"\n");
156 printf("# Canon digital camera (EOS 3D) that looks like MTP device (PID 0x3113)\n");
157 printf("ATTR{idVendor}==\"04a9\", ATTR{idProduct}==\"3113\", GOTO=\"libmtp_rules_end\"\n");
158 printf("# Sensitive Atheros devices that look like MTP devices\n");
159 printf("ATTR{idVendor}==\"0cf3\", GOTO=\"libmtp_rules_end\"\n");
160 printf("# Sensitive Atmel JTAG programmers\n");
161 printf("ATTR{idVendor}==\"03eb\", GOTO=\"libmtp_rules_end\"\n");
162 printf("# Sensitive Philips device\n");
163 printf("ATTR{idVendor}==\"0471\", ATTR{idProduct}==\"083f\", GOTO=\"libmtp_rules_end\"\n");
164 break;
165 case style_udev_old:
166 printf("# UDEV-style hotplug map for libmtp\n");
167 printf("# Put this file in /etc/udev/rules.d\n\n");
168 printf("ACTION!=\"add\", GOTO=\"libmtp_rules_end\"\n");
169 printf("ENV{MAJOR}!=\"?*\", GOTO=\"libmtp_rules_end\"\n");
170 printf("SUBSYSTEM==\"usb_device\", GOTO=\"libmtp_usb_device_rules\"\n"
171 "GOTO=\"libmtp_rules_end\"\n\n"
172 "LABEL=\"libmtp_usb_device_rules\"\n\n");
173 break;
174 case style_usbmap:
175 printf("# This usermap will call the script \"libmtp.sh\" whenever a known MTP device is attached.\n\n");
176 break;
177 case style_hal:
178 printf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> <!-- -*- SGML -*- -->\n");
179 printf("<!-- This file was generated by %s - - fdi -->\n", argv[0]);
180 printf("<deviceinfo version=\"0.2\">\n");
181 printf(" <device>\n");
182 printf(" <match key=\"info.subsystem\" string=\"usb\">\n");
183 break;
184 case style_usbids:
185 printf("# usb.ids style device list from libmtp\n");
186 printf("# Compare: http://www.linux-usb.org/usb.ids\n");
187 break;
188 case style_hwdb:
189 printf("# hardware database file for libmtp supported devices\n");
190 break;
191 }
192
193 for (i = 0; i < numentries; i++) {
194 LIBMTP_device_entry_t * entry = &entries[i];
195
196 switch (style) {
197 case style_udev:
198 case style_udev_old:
199 printf("# %s %s\n", entry->vendor, entry->product);
200 printf("ATTR{idVendor}==\"%04x\", ATTR{idProduct}==\"%04x\", %s", entry->vendor_id, entry->product_id, action);
201 if (udev_group != NULL) printf(", GROUP=\"%s\"", udev_group);
202 if (udev_mode != NULL) printf(", MODE=\"%s\"", udev_mode);
203 printf("\n");
204 break;
205 case style_usbmap:
206 printf("# %s %s\n", entry->vendor, entry->product);
207 printf("libmtp.sh 0x0003 0x%04x 0x%04x 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000\n", entry->vendor_id, entry->product_id);
208 break;
209 case style_hal:
210 printf(" <!-- %s %s -->\n", entry->vendor, entry->product);
211 printf(" <match key=\"usb.vendor_id\" int=\"0x%04x\">\n", entry->vendor_id);
212 printf(" <match key=\"usb.product_id\" int=\"0x%04x\">\n", entry->product_id);
213 /* FIXME: If hal >=0.5.10 can be depended upon, the matches below with contains_not can instead use addset */
214 printf(" <match key=\"info.capabilities\" contains_not=\"portable_audio_player\">\n");
215 printf(" <append key=\"info.capabilities\" type=\"strlist\">portable_audio_player</append>\n");
216 printf(" </match>\n");
217 printf(" <merge key=\"info.vendor\" type=\"string\">%s</merge>\n", entry->vendor);
218 printf(" <merge key=\"info.product\" type=\"string\">%s</merge>\n", entry->product);
219 printf(" <merge key=\"info.category\" type=\"string\">portable_audio_player</merge>\n");
220 printf(" <merge key=\"portable_audio_player.access_method\" type=\"string\">user</merge>\n");
221 printf(" <match key=\"portable_audio_player.access_method.protocols\" contains_not=\"mtp\">\n");
222 printf(" <append key=\"portable_audio_player.access_method.protocols\" type=\"strlist\">mtp</append>\n");
223 printf(" </match>\n");
224 printf(" <append key=\"portable_audio_player.access_method.drivers\" type=\"strlist\">libmtp</append>\n");
225 /* FIXME: needs true list of formats ... But all of them can do MP3 and WMA */
226 printf(" <match key=\"portable_audio_player.output_formats\" contains_not=\"audio/mpeg\">\n");
227 printf(" <append key=\"portable_audio_player.output_formats\" type=\"strlist\">audio/mpeg</append>\n");
228 printf(" </match>\n");
229 printf(" <match key=\"portable_audio_player.output_formats\" contains_not=\"audio/x-ms-wma\">\n");
230 printf(" <append key=\"portable_audio_player.output_formats\" type=\"strlist\">audio/x-ms-wma</append>\n");
231 printf(" </match>\n");
232 /* Special hack to support the OGG format - irivers, TrekStor and NormSoft (Palm) can always play these files! */
233 if (entry->vendor_id == 0x4102 || // iriver
234 entry->vendor_id == 0x066f || // TrekStor
235 entry->vendor_id == 0x1703) { // NormSoft, Inc.
236 printf(" <match key=\"portable_audio_player.output_formats\" contains_not=\"application/ogg\">\n");
237 printf(" <append key=\"portable_audio_player.output_formats\" type=\"strlist\">application/ogg</append>\n");
238 printf(" </match>\n");
239 }
240 printf(" <merge key=\"portable_audio_player.libmtp.protocol\" type=\"string\">mtp</merge>\n");
241 printf(" </match>\n");
242 printf(" </match>\n");
243 break;
244 case style_usbids:
245 if (last_vendor != entry->vendor_id) {
246 printf("%04x\n", entry->vendor_id);
247 }
248 printf("\t%04x %s %s\n", entry->product_id, entry->vendor, entry->product);
249 break;
250 case style_hwdb:
251 printf("# %s %s\n", entry->vendor, entry->product);
252 printf("usb:v%04xp%04x*\n", entry->vendor_id, entry->product_id);
253 printf(" ID_MEDIA_PLAYER=1\n");
254 printf(" ID_MTP_DEVICE=1\n");
255 printf("\n");
256 break;
257 }
258 last_vendor = entry->vendor_id;
259 }
260 } else {
261 printf("Error.\n");
262 exit(1);
263 }
264
265 // Then the footer.
266 switch (style) {
267 case style_usbmap:
268 case style_hwdb:
269 break;
270 case style_udev:
271 case style_udev_old:
272 /*
273 * This is code that invokes the mtp-probe program on
274 * every USB device that is either PTP or vendor specific
275 */
276 printf("\n# Autoprobe vendor-specific, communication and PTP devices\n");
277 printf("ENV{ID_MTP_DEVICE}!=\"1\", ENV{MTP_NO_PROBE}!=\"1\", ENV{COLOR_MEASUREMENT_DEVICE}!=\"1\", ENV{libsane_matched}!=\"yes\", ATTR{bDeviceClass}==\"00|02|06|ef|ff\", PROGRAM=\"%smtp-probe /sys$env{DEVPATH} $attr{busnum} $attr{devnum}\", RESULT==\"1\", %s", mtp_probe_dir, action);
278 if (udev_group != NULL) printf(", GROUP=\"%s\"", udev_group);
279 if (udev_mode != NULL) printf(", MODE=\"%s\"", udev_mode);
280 printf("\n");
281 printf("\nLABEL=\"libmtp_rules_end\"\n");
282 break;
283 case style_hal:
284 printf(" </match>\n");
285 printf(" </device>\n");
286 printf("</deviceinfo>\n");
287 break;
288 case style_usbids:
289 printf("\n");
290 }
291
292 exit (0);
293 }
294