Home | History | Annotate | Download | only in util
      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