1/**
2 * \file hotplug.c
3 * Example program to create hotplug scripts.
4 *
5 * Copyright (C) 2005-2007 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 "common.h"
24#include <unistd.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28
29static void usage(void)
30{
31  fprintf(stderr, "usage: hotplug [-u -H -i -a\"ACTION\"]\n");
32  fprintf(stderr, "       -u:  use udev syntax\n");
33  fprintf(stderr, "       -H:  use hal syntax\n");
34  fprintf(stderr, "       -i:  use usb.ids simple list syntax\n");
35  fprintf(stderr, "       -a\"ACTION\": perform udev action ACTION on attachment\n");
36  exit(1);
37}
38
39enum style {
40  style_usbmap,
41  style_udev,
42  style_hal,
43  style_usbids
44};
45
46int main (int argc, char **argv)
47{
48  LIBMTP_device_entry_t *entries;
49  int numentries;
50  int i;
51  int ret;
52  enum style style = style_usbmap;
53  int opt;
54  extern int optind;
55  extern char *optarg;
56  char *udev_action = NULL;
57  char default_udev_action[] = "SYMLINK+=\"libmtp-%k\", MODE=\"666\"";
58  char *action; // To hold the action actually used.
59  uint16_t last_vendor = 0x0000U;
60
61  while ( (opt = getopt(argc, argv, "uUiHa:")) != -1 ) {
62    switch (opt) {
63    case 'a':
64      udev_action = strdup(optarg);
65    case 'u':
66      style = style_udev;
67      break;
68    case 'H':
69      style = style_hal;
70      break;
71    case 'i':
72      style = style_usbids;
73      break;
74    default:
75      usage();
76    }
77  }
78
79  if (udev_action != NULL) {
80    action = udev_action;
81  } else {
82    action = default_udev_action;
83  }
84
85  LIBMTP_Init();
86  ret = LIBMTP_Get_Supported_Devices_List(&entries, &numentries);
87  if (ret == 0) {
88    switch (style) {
89    case style_udev:
90      printf("# UDEV-style hotplug map for libmtp\n");
91      printf("# Put this file in /etc/udev/rules.d\n\n");
92      printf("ACTION!=\"add\", GOTO=\"libmtp_rules_end\"\n");
93      printf("ENV{MAJOR}!=\"?*\", GOTO=\"libmtp_rules_end\"\n");
94      printf("SUBSYSTEM==\"usb\", GOTO=\"libmtp_usb_rules\"\n"
95	     "# The following thing will be deprecated when older kernels are phased out.\n"
96             "SUBSYSTEM==\"usb_device\", GOTO=\"libmtp_usb_device_rules\"\n"
97	     "GOTO=\"libmtp_rules_end\"\n\n"
98	     "LABEL=\"libmtp_usb_rules\"\n\n");
99      break;
100    case style_usbmap:
101      printf("# This usermap will call the script \"libmtp.sh\" whenever a known MTP device is attached.\n\n");
102      break;
103    case style_hal:
104      printf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> <!-- -*- SGML -*- -->\n");
105      printf("<!-- This file was generated by %s - - fdi -->\n", argv[0]);
106      printf("<deviceinfo version=\"0.2\">\n");
107      printf("  <device>\n");
108      printf("    <match key=\"info.subsystem\" string=\"usb\">\n");
109      break;
110    case style_usbids:
111      printf("# usb.ids style device list from libmtp\n");
112      printf("# Compare: http://www.linux-usb.org/usb.ids\n");
113      break;
114    }
115
116    for (i = 0; i < numentries; i++) {
117      LIBMTP_device_entry_t * entry = &entries[i];
118
119      switch (style) {
120      case style_udev:
121	{
122          printf("# %s %s\n", entry->vendor, entry->product);
123	  // Old style directly SYSFS named.
124	  // printf("SYSFS{idVendor}==\"%04x\", SYSFS{idProduct}==\"%04x\", %s\n", entry->vendor_id, entry->product_id, action);
125	  // Newer style
126	  printf("ATTR{idVendor}==\"%04x\", ATTR{idProduct}==\"%04x\", %s\n", entry->vendor_id, entry->product_id, action);
127	  break;
128        }
129      case style_usbmap:
130          printf("# %s %s\n", entry->vendor, entry->product);
131          printf("libmtp.sh    0x0003  0x%04x  0x%04x  0x0000  0x0000  0x00    0x00    0x00    0x00    0x00    0x00    0x00000000\n", entry->vendor_id, entry->product_id);
132          break;
133        case style_hal:
134          printf("      <!-- %s %s -->\n", entry->vendor, entry->product);
135          printf("      <match key=\"usb.vendor_id\" int=\"0x%04x\">\n", entry->vendor_id);
136          printf("        <match key=\"usb.product_id\" int=\"0x%04x\">\n", entry->product_id);
137          /* FIXME: If hal >=0.5.10 can be depended upon, the matches below with contains_not can instead use addset */
138          printf("          <match key=\"info.capabilities\" contains_not=\"portable_audio_player\">\n");
139          printf("            <append key=\"info.capabilities\" type=\"strlist\">portable_audio_player</append>\n");
140          printf("          </match>\n");
141          printf("          <merge key=\"info.vendor\" type=\"string\">%s</merge>\n", entry->vendor);
142          printf("          <merge key=\"info.product\" type=\"string\">%s</merge>\n", entry->product);
143          printf("          <merge key=\"info.category\" type=\"string\">portable_audio_player</merge>\n");
144          printf("          <merge key=\"portable_audio_player.access_method\" type=\"string\">user</merge>\n");
145          printf("          <match key=\"portable_audio_player.access_method.protocols\" contains_not=\"mtp\">\n");
146          printf("            <append key=\"portable_audio_player.access_method.protocols\" type=\"strlist\">mtp</append>\n");
147          printf("          </match>\n");
148          printf("          <append key=\"portable_audio_player.access_method.drivers\" type=\"strlist\">libmtp</append>\n");
149          /* FIXME: needs true list of formats ... But all of them can do MP3 and WMA */
150          printf("          <match key=\"portable_audio_player.output_formats\" contains_not=\"audio/mpeg\">\n");
151          printf("            <append key=\"portable_audio_player.output_formats\" type=\"strlist\">audio/mpeg</append>\n");
152          printf("          </match>\n");
153          printf("          <match key=\"portable_audio_player.output_formats\" contains_not=\"audio/x-ms-wma\">\n");
154          printf("            <append key=\"portable_audio_player.output_formats\" type=\"strlist\">audio/x-ms-wma</append>\n");
155          printf("          </match>\n");
156	  /* Special hack to support the OGG format - irivers, TrekStor and NormSoft (Palm) can always play these files! */
157	  if (entry->vendor_id == 0x4102 || // iriver
158	      entry->vendor_id == 0x066f || // TrekStor
159	      entry->vendor_id == 0x1703) { // NormSoft, Inc.
160	    printf("          <match key=\"portable_audio_player.output_formats\" contains_not=\"application/ogg\">\n");
161	    printf("            <append key=\"portable_audio_player.output_formats\" type=\"strlist\">application/ogg</append>\n");
162	    printf("          </match>\n");
163	  }
164          printf("          <merge key=\"portable_audio_player.libmtp.protocol\" type=\"string\">mtp</merge>\n");
165          printf("        </match>\n");
166          printf("      </match>\n");
167        break;
168        case style_usbids:
169          if (last_vendor != entry->vendor_id) {
170            printf("%04x\n", entry->vendor_id);
171          }
172          printf("\t%04x  %s %s\n", entry->product_id, entry->vendor, entry->product);
173        break;
174      }
175      last_vendor = entry->vendor_id;
176    }
177  } else {
178    printf("Error.\n");
179    exit(1);
180  }
181
182  // For backward comparibility with the #$!+@! ever changing
183  // udev rule style...
184  if (style == style_udev) {
185    printf("GOTO=\"libmtp_rules_end\"\n\n");
186    printf("LABEL=\"libmtp_usb_device_rules\"\n");
187    for (i = 0; i < numentries; i++) {
188      LIBMTP_device_entry_t * entry = &entries[i];
189
190      printf("# %s %s\n", entry->vendor, entry->product);
191      printf("ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", %s\n", entry->vendor_id, entry->product_id, action);
192    }
193    printf("GOTO=\"libmtp_rules_end\"\n\n");
194  }
195
196  // Then the footer.
197  switch (style) {
198  case style_usbmap:
199    break;
200  case style_udev:
201    printf("LABEL=\"libmtp_rules_end\"\n");
202    break;
203  case style_hal:
204    printf("    </match>\n");
205    printf("  </device>\n");
206    printf("</deviceinfo>\n");
207    break;
208  case style_usbids:
209    printf("\n");
210  }
211
212  exit (0);
213}
214