1 /** 2 * \file mtp-hotplug.c 3 * Program to create hotplug scripts. 4 * 5 * Copyright (C) 2005-2012 Linus Walleij <triad (at) df.lth.se> 6 * Copyright (C) 2006-2008 Marcus Meissner <marcus (at) 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 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 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