<commit_before>/*
 * \file libusb-glue.c
 * Low-level USB interface glue towards libusb.
 *
 * Copyright (C) 2005-2007 <NAME> <<EMAIL>>
 * Copyright (C) 2005-2007 <NAME> <<EMAIL>>
 * Copyright (C) 2006-2007 <NAME>
 * Copyright (C) 2007 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Created by <NAME> on 24/12/2005. (as mtp-utils.c)
 * Modified by <NAME> 2006-03-06
 *  (Notice that Anglo-Saxons use little-endian dates and Swedes 
 *   use big-endian dates.)
 *
 */
#include "libmtp.h"
#include "libusb-glue.h"
#include "util.h"
#include "ptp.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <usb.h>

#include "ptp-pack.c"

/* To enable debug prints, switch on this */
//#define ENABLE_USB_BULK_DEBUG

/* OUR APPLICATION USB URB (2MB) ;) */
#define PTPCAM_USB_URB		2097152

/* this must not be too short - the original 4000 was not long
   enough for big file transfers. I imagine the player spends a 
   bit of time gearing up to receiving lots of data. This also makes
   connecting/disconnecting more reliable */
#define USB_TIMEOUT		10000
#define USB_CAPTURE_TIMEOUT	20000

/* USB control message data phase direction */
#ifndef USB_DP_HTD
#define USB_DP_HTD		(0x00 << 7)	/* host to device */
#endif
#ifndef USB_DP_DTH
#define USB_DP_DTH		(0x01 << 7)	/* device to host */
#endif

/* USB Feature selector HALT */
#ifndef USB_FEATURE_HALT
#define USB_FEATURE_HALT	0x00
#endif

/*
 * MTP device list, trying real bad to get all devices into
 * this list by stealing from everyone I know.
 */
static const LIBMTP_device_entry_t mtp_device_table[] = {
  
  /*
   * Creative Technology
   * Initially the Creative devices was all we supported so these are
   * the most thoroughly tested devices.
   */
  { "Creative Zen Vision", 0x041e, 0x411f, DEVICE_FLAG_NONE },
  { "Creative Portable Media Center", 0x041e, 0x4123, DEVICE_FLAG_NONE },
  { "Creative Zen Xtra (MTP mode)", 0x041e, 0x4128, DEVICE_FLAG_NONE },
  { "Second generation Dell DJ", 0x041e, 0x412f, DEVICE_FLAG_NONE },
  { "Creative Zen Micro (MTP mode)", 0x041e, 0x4130, DEVICE_FLAG_NONE },
  { "Creative Zen Touch (MTP mode)", 0x041e, 0x4131, DEVICE_FLAG_NONE },
  { "Dell Pocket DJ (MTP mode)", 0x041e, 0x4132, DEVICE_FLAG_NONE },
  { "Creative Zen Sleek (MTP mode)", 0x041e, 0x4137, DEVICE_FLAG_NONE },
  { "Creative Zen MicroPhoto", 0x041e, 0x413c, DEVICE_FLAG_NONE },
  { "Creative Zen Sleek Photo", 0x041e, 0x413d, DEVICE_FLAG_NONE },
  { "Creative Zen Vision:M", 0x041e, 0x413e, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Creative Zen V", 0x041e, 0x4150, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Creative Zen Vision:M (DVP-HD0004)", 0x041e, 0x4151, DEVICE_FLAG_NONE },
  // Reported by Darel on the XNJB forums
  { "Creative Zen V Plus", 0x041e, 0x4152, DEVICE_FLAG_NONE },
  { "Creative Zen Vision W", 0x041e, 0x4153, DEVICE_FLAG_NONE },
  // Reported by Ringofan <<EMAIL>>
  { "Creative Zen V 2GB", 0x041e, 0x4158, DEVICE_FLAG_NONE },

  /*
   * Samsung
   * We suspect that more of these are dual mode.
   */
  // From <NAME>
  { "Samsung YH-920", 0x04e8, 0x5022, DEVICE_FLAG_UNLOAD_DRIVER },
  // Contributed by aronvanammers on SourceForge
  { "Samsung YH-925GS", 0x04e8, 0x5024, DEVICE_FLAG_NONE },
  // From libgphoto2
  { "Samsung YH-820", 0x04e8, 0x502e, DEVICE_FLAG_NONE },
  // Contributed by <EMAIL>
  { "Samsung YH-925(-GS)", 0x04e8, 0x502f, DEVICE_FLAG_NONE },
  // Contributed by anonymous person on SourceForge
  { "Samsung YH-J70J", 0x04e8, 0x5033, DEVICE_FLAG_UNLOAD_DRIVER },
  // From XNJB user
  { "Samsung YP-Z5", 0x04e8, 0x503c, DEVICE_FLAG_NONE },
  // From XNJB user
  { "Samsung YP-Z5 2GB", 0x04e8, 0x5041, DEVICE_FLAG_NONE },
  // Contributed by anonymous person on SourceForge
  { "Samsung YP-T7J", 0x04e8, 0x5047, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Samsung YP-U2J (YP-U2JXB/XAA)", 0x04e8, 0x5054, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by <NAME>
  { "Samsung YP-F2J", 0x04e8, 0x5057, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by Patrick <<EMAIL>>
  { "Samsung YP-K5", 0x04e8, 0x505a, DEVICE_FLAG_NO_ZERO_READS },
  // From <EMAIL> - 0x4e8/0x507c is the UMS mode, don't add this.
  // From m.eik michalke
  { "Samsung YP-U3", 0x04e8, 0x507d, DEVICE_FLAG_NONE },
  // Reported by <NAME> <<EMAIL>>
  { "Samsung YP-T9", 0x04e8, 0x507f, DEVICE_FLAG_NONE },
  // From <NAME>
  { "Samsung YP-K3", 0x04e8, 0x5081, DEVICE_FLAG_NONE },
  // From a rouge .INF file,
  // this device ID seems to have been recycled for the Samsung SGH-A707 Cingular cellphone
  { "Samsung YH-999 Portable Media Center / Samsung SGH-A707", 0x04e8, 0x5a0f, DEVICE_FLAG_NONE },
  // From <NAME>
  { "Samsung X830 Mobile Phone", 0x04e8, 0x6702, DEVICE_FLAG_NONE },
  // From James <<EMAIL>>
  { "Samsung U600 Mobile Phone", 0x04e8, 0x6709, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * Intel
   */
  { "Intel Bandon Portable Media Center", 0x045e, 0x00c9, DEVICE_FLAG_NONE },

  /*
   * JVC
   */
  // From <NAME>
  { "JVC Alneo XA-HD500", 0x04f1, 0x6105, DEVICE_FLAG_NONE },

  /*
   * Philips
   */
  { "Philips HDD6320/00 and HDD6330/17", 0x0471, 0x014b, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Anonymous SourceForge user
  { "Philips HDD1630/17", 0x0471, 0x014c, DEVICE_FLAG_NONE },
  // from discussion forum
  { "Philips HDD085/00 and HDD082/17", 0x0471, 0x014d, DEVICE_FLAG_NONE },
  // from XNJB forum
  { "Philips GoGear SA9200", 0x0471, 0x014f, DEVICE_FLAG_NONE },
  // From <NAME>
  { "Philips GoGear Audio", 0x0471, 0x0165, DEVICE_FLAG_NONE },
  // from <NAME> <<EMAIL>>
  { "Philips Shoqbox", 0x0471, 0x0172, DEVICE_FLAG_ONLY_7BIT_FILENAMES },
  // from npedrosa
  { "Philips PSA610", 0x0471, 0x0181, DEVICE_FLAG_NONE },
  // From libgphoto2 source
  { "Philips HDD6320", 0x0471, 0x01eb, DEVICE_FLAG_NONE },
  // from XNJB user
  { "Philips PSA235", 0x0471, 0x7e01, DEVICE_FLAG_NONE },


  /*
   * SanDisk
   * several devices (c150 for sure) are definately dual-mode and must 
   * have the USB mass storage driver that hooks them unloaded first.
   * They all have problematic dual-mode making the device unload effect
   * uncertain on these devices. All except for the Linux based ones seem
   * to need DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL.
   */
  // Reported by <NAME>
  { "SanDisk Sansa m230/m240", 0x0781, 0x7400, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL},
  // Reported by <EMAIL>
  { "SanDisk Sansa c150", 0x0781, 0x7410, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // From libgphoto2 source
  // Reported by <<EMAIL>.sourceforge.net>
  // Reported by <NAME> <<EMAIL>>
  { "SanDisk Sansa e200/e250/e260/e270/e280", 0x0781, 0x7420, 
    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by XNJB user
  { "SanDisk Sansa e280", 0x0781, 0x7421, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by anonymous user at sourceforge.net
  { "SanDisk Sansa c240/c250", 0x0781, 0x7450, 
    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by XNJB user, and <NAME> <<EMAIL>>
  // This has no dual-mode so no need to unload any driver.
  // This is a Linux based device!
  { "SanDisk Sansa Connect", 0x0781, 0x7480, DEVICE_FLAG_NONE },
  // Reported by <NAME> Jr.
  { "SanDisk Sansa Express", 0x0781, 0x7460, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
  // Reported by XNJB user
  { "SanDisk Sansa m240", 0x0781, 0x7430, 
    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  

  /*
   * iRiver
   * we assume that PTP_OC_MTP_GetObjPropList is essentially
   * broken on all iRiver devices, meaning it simply won't return
   * all properties for a file when asking for metadata 0xffffffff. 
   * Please test on your device if you believe it isn't broken!
   * Some devices from http://www.mtp-ums.net/viewdeviceinfo.php
   */
  { "iRiver Portable Media Center", 0x1006, 0x4002, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver Portable Media Center", 0x1006, 0x4003, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // From an anonymous person at SourceForge
  { "iRiver iFP-880", 0x4102, 0x1008, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // From libgphoto2 source
  { "iRiver T10", 0x4102, 0x1113, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T20 FM", 0x4102, 0x1114, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // This appears at the MTP-UMS site
  { "iRiver T20", 0x4102, 0x1115, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver U10", 0x4102, 0x1116, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T10", 0x4102, 0x1117, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T20", 0x4102, 0x1118, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T30", 0x4102, 0x1119, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME>
  { "iRiver T10 2GB", 0x4102, 0x1120, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Rough guess this is the MTP device ID...
  { "iRiver N12", 0x4102, 0x1122, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME> <<EMAIL>>
  // Newer iriver devices seem to have shaped-up firmware without any
  // of the annoying bugs.
  { "iRiver Clix2", 0x4102, 0x1126, DEVICE_FLAG_NONE },
  // Reported by <NAME>
  { "iRiver Clix", 0x4102, 0x112a, 
    DEVICE_FLAG_NO_ZERO_READS | DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME> <<EMAIL>>
  { "iRiver X20", 0x4102, 0x1132, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME> <<EMAIL>>
  { "iRiver T60", 0x4102, 0x1134, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME>
  { "iRiver H10 20GB", 0x4102, 0x2101, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver H10", 0x4102, 0x2102, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },


  /*
   * Dell
   */
  { "Dell DJ Itty", 0x413c, 0x4500, DEVICE_FLAG_NONE },
  
  /*
   * Toshiba
   */
  { "Toshiba Gigabeat MEGF-40", 0x0930, 0x0009, DEVICE_FLAG_NONE },
  { "Toshiba Gigabeat", 0x0930, 0x000c, DEVICE_FLAG_NONE },
  // Reported by <NAME>
  { "Toshiba Gigabeat P20", 0x0930, 0x000f, DEVICE_FLAG_NONE },
  // From libgphoto2
  { "Toshiba Gigabeat S", 0x0930, 0x0010, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by <NAME>
  { "Toshiba Gigabeat P10", 0x0930, 0x0011, DEVICE_FLAG_NONE },
  // Reported by <NAME> <<EMAIL>>
  { "Toshiba Gigabeat U", 0x0930, 0x0016, DEVICE_FLAG_NONE },
  
  /*
   * Archos
   * These devices have some dual-mode interfaces which will really
   * respect the driver unloading, so DEVICE_FLAG_UNLOAD_DRIVER
   * really work on these devices!
   */
  // Reported by <NAME> <<EMAIL>>
  { "Archos Gmini XS100", 0x0e79, 0x1207, DEVICE_FLAG_UNLOAD_DRIVER },
  // Added by <NAME>
  { "Archos XS202 (MTP mode)", 0x0e79, 0x1208, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Archos 104 (MTP mode)", 0x0e79, 0x120a, DEVICE_FLAG_NONE },
  // Reported by <NAME> <<EMAIL>>
  { "Archos 504 (MTP mode)", 0x0e79, 0x1307, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by <NAME> <<EMAIL>>
  { "Archos 704 mobile dvr", 0x0e79, 0x130d, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * Dunlop (OEM of EGOMAN ltd?) reported by Nanomad
   * This unit is falsely detected as USB mass storage in Linux
   * prior to kernel 2.6.19 (fixed by patch from <NAME>)
   * so on older kernels special care is needed to remove the
   * USB mass storage driver that erroneously binds to the device
   * interface.
   */
  { "Dunlop MP3 player 1GB / EGOMAN MD223AFD", 0x10d6, 0x2200, DEVICE_FLAG_UNLOAD_DRIVER},
  
  /*
   * Microsoft
   */
  // Reported by <NAME>
  { "Microsoft Zune", 0x045e, 0x0710, DEVICE_FLAG_NONE }, 
  
  /*
   * Sirius
   */
  { "Sirius Stiletto", 0x18f6, 0x0102, DEVICE_FLAG_NONE },

  /*
   * Canon
   * This is actually a camera, but it has a Microsoft device descriptor
   * and reports itself as supporting the MTP extension.
   */
  {"Canon PowerShot A640 (PTP/MTP mode)", 0x04a9, 0x3139, DEVICE_FLAG_NONE },

  /*
   * Nokia
   */
  // From: <NAME> <<EMAIL>>
  {"Nokia 5300 Mobile Phone", 0x0421, 0x04ba, DEVICE_FLAG_NONE },
  // From <NAME> <<EMAIL>>
  {"Nokia N73 Mobile Phone", 0x0421, 0x04d1, DEVICE_FLAG_UNLOAD_DRIVER },
  // From Swapan <<EMAIL>>
  {"Nokia N75 Mobile Phone", 0x0421, 0x04e1, DEVICE_FLAG_NONE },
  // From: <NAME> <<EMAIL>>
  {"Nokia N80 Internet Edition (Media Player)", 0x0421, 0x04f1, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * LOGIK
   * Sold in the UK, seem to be manufactured by CCTech in China.
   */
  {"Logik LOG DAX MP3 and DAB Player", 0x13d1, 0x7002, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * RCA / Thomson
   */
  {"Thomson Opal / RCA Lyra MC4002", 0x069b, 0x0777, DEVICE_FLAG_NONE },
  
  /*
   * NTT DoCoMo
   */
  {"FOMA F903iX HIGH-SPEED", 0x04c5, 0x1140, DEVICE_FLAG_NONE },

  /*
   * Palm device userland program named Pocket Tunes
   * Reported by <NAME> <<EMAIL>>
   */
  {"Palm / Handspring Pocket Tunes", 0x1703, 0x0001, DEVICE_FLAG_NONE },
  // Reported by anonymous submission
  {"Palm / Handspring Pocket Tunes 4", 0x1703, 0x0002, DEVICE_FLAG_NONE },

  /*
   * TrekStor devices
   * Their datasheet claims their devices are dualmode so probably needs to
   * unload the attached drivers here.
   */
  // Reported by <NAME> <<EMAIL>>
  {"TrekStor Vibez i.Beat sweez FM", 0x0402, 0x0611, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by <NAME> <<EMAIL>>
  {"TrekStor Vibez 8/12GB", 0x066f, 0x842a, DEVICE_FLAG_UNLOAD_DRIVER },
  
  /*
   * Disney (have had no reports of this actually working.)
   */
  // Reported by XNJB user
  {"Disney MixMax", 0x0aa6, 0x6021, DEVICE_FLAG_NONE },

  /*
   * Cowon Systems, Inc.
   * The iAudio audiophile devices don't encourage the use of MTP.
   */
  // Reported by <NAME> <<EMAIL>>
  {"Cowon iAudio U3 (MTP mode)", 0x0e21, 0x0701, DEVICE_FLAG_NONE },
  // Reported by <NAME>
  {"Cowon iAudio 7 (MTP mode)", 0x0e21, 0x0751, DEVICE_FLAG_NONE },
  // Reported by TJ Something <<EMAIL>>
  {"Cowon iAudio D2 (MTP mode)", 0x0e21, 0x0801, 
   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },

  /*
   * Insignia, dual-mode.
   */
  { "Insignia NS-DV45", 0x19ff, 0x0303, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * LG Electronics
   */
  // Not verified - anonymous submission
  { "LG UP3", 0x043e, 0x70b1, DEVICE_FLAG_NONE },
  
  /*
   * Other strange stuff.
   */
  {"Isabella's prototype", 0x0b20, 0xddee, DEVICE_FLAG_NONE }
};
static const int mtp_device_table_size = sizeof(mtp_device_table) / sizeof(LIBMTP_device_entry_t);

int ptpcam_usb_timeout = USB_TIMEOUT;

// Local functions
static struct usb_bus* init_usb();
static void close_usb(PTP_USB* ptp_usb);
static void find_interface_and_endpoints(struct usb_device *dev,
					 uint8_t *interface,
					 int* inep, 
					 int* inep_maxpacket, 
					 int* outep, 
					 int* outep_maxpacket, 
					 int* intep);
static void clear_stall(PTP_USB* ptp_usb);
static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev);
static short ptp_write_func (unsigned long,PTPDataHandler*,void *data,unsigned long*);
static short ptp_read_func (unsigned long,PTPDataHandler*,void *data,unsigned long*,int);
static int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep);
static int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status);

/**
 * Get a list of the supported USB devices.
 *
 * The developers depend on users of this library to constantly
 * add in to the list of supported devices. What we need is the
 * device name, USB Vendor ID (VID) and USB Product ID (PID).
 * put this into a bug ticket at the project homepage, please.
 * The VID/PID is used to let e.g. udev lift the device to
 * console userspace access when it's plugged in.
 *
 * @param devices a pointer to a pointer that will hold a device
 *        list after the call to this function, if it was
 *        successful.
 * @param numdevs a pointer to an integer that will hold the number
 *        of devices in the device list if the call was successful.
 * @return 0 if the list was successfull retrieved, any other
 *        value means failure.
 */
int LIBMTP_Get_Supported_Devices_List(LIBMTP_device_entry_t ** const devices, int * const numdevs)
{
  *devices = (LIBMTP_device_entry_t *) &mtp_device_table;
  *numdevs = mtp_device_table_size;
  return 0;
}


static struct usb_bus* init_usb()
{
  usb_init();
  usb_find_busses();
  usb_find_devices();
  return (usb_get_busses());
}

/**
 * Small recursive function to append a new usb_device to the linked list of
 * USB MTP devices
 * @param devlist dynamic linked list of pointers to usb devices with MTP 
 * properties.
 * @param next New USB MTP device to be added to list
 * @return nothing
 */
static mtpdevice_list_t *append_to_mtpdevice_list(mtpdevice_list_t *devlist,
				     struct usb_device *newdevice)
{
  mtpdevice_list_t *new_list_entry;
  
  new_list_entry = (mtpdevice_list_t *) malloc(sizeof(mtpdevice_list_t));
  if (new_list_entry == NULL) {
    return NULL;
  }
  // Fill in USB device, if we *HAVE* to make a copy of the device do it here.
  new_list_entry->libusb_device = newdevice;
  new_list_entry->next = NULL;
  
  if (devlist == NULL) {
    return new_list_entry;
  } else {
    mtpdevice_list_t *tmp = devlist;
    while (tmp->next != NULL) {
      tmp = tmp->next;
    }
    tmp->next = new_list_entry;
  }
  return devlist;
}

/**
 * Small recursive function to free dynamic memory allocated to the linked list
 * of USB MTP devices
 * @param devlist dynamic linked list of pointers to usb devices with MTP 
 * properties.
 * @return nothing
 */
void free_mtpdevice_list(mtpdevice_list_t *devlist)
{
  mtpdevice_list_t *tmplist = devlist;

  if (devlist == NULL)
    return;
  while (tmplist != NULL) {
    mtpdevice_list_t *tmp = tmplist;
    tmplist = tmplist->next;
    // Do not free() the fields (ptp_usb, parms)! These are used elsewhere.
    free(tmp);
  }
  return;
}

/**
 * This checks if a device has an MTP descriptor. The descriptor was
 * elaborated about in gPhoto bug 1482084, and some official documentation
 * with no strings attached was published by Microsoft at
 * http://www.microsoft.com/whdc/system/bus/USB/USBFAQ_intermed.mspx#E3HAC
 *
 * @param dev a device struct from libusb.
 * @param dumpfile set to non-NULL to make the descriptors dump out
 *        to this file in human-readable hex so we can scruitinze them.
 * @return 1 if the device is MTP compliant, 0 if not.
 */
static int probe_device_descriptor(struct usb_device *dev, FILE *dumpfile)
{
  usb_dev_handle *devh;
  unsigned char buf[1024], cmd;
  int ret;
  
  /* Don't examine hubs (no point in that) */
  if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) {
    return 0;
  }
  
  /* Attempt to open Device on this port */
  devh = usb_open(dev);
  if (devh == NULL) {
    /* Could not open this device */
    return 0;
  }
  
  /* Read the special descriptor */
  ret = usb_get_descriptor(devh, 0x03, 0xee, buf, sizeof(buf));

  // Dump it, if requested
  if (dumpfile != NULL && ret > 0) {
    fprintf(dumpfile, "Microsoft device descriptor 0xee:\n");
    data_dump_ascii(dumpfile, buf, ret, 16);
  }
  
  /* Check if descriptor length is at least 10 bytes */
  if (ret < 10) {
    usb_close(devh);
    return 0;
  }
      
  /* Check if this device has a Microsoft Descriptor */
  if (!((buf[2] == 'M') && (buf[4] == 'S') &&
	(buf[6] == 'F') && (buf[8] == 'T'))) {
    usb_close(devh);
    return 0;
  }
      
  /* Check if device responds to control message 1 or if there is an error */
  cmd = buf[16];
  ret = usb_control_msg (devh,
			 USB_ENDPOINT_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR,
			 cmd,
			 0,
			 4,
			 (char *) buf,
			 sizeof(buf),
			 1000);

  // Dump it, if requested
  if (dumpfile != NULL && ret > 0) {
    fprintf(dumpfile, "Microsoft device response to control message 1, CMD 0x%02x:\n", cmd);
    data_dump_ascii(dumpfile, buf, ret, 16);
  }
  
  /* If this is true, the device either isn't MTP or there was an error */
  if (ret <= 0x15) {
    /* TODO: If there was an error, flag it and let the user know somehow */
    /* if(ret == -1) {} */
    usb_close(devh);
    return 0;
  }
  
  /* Check if device is MTP or if it is something like a USB Mass Storage 
     device with Janus DRM support */
  if ((buf[0x12] != 'M') || (buf[0x13] != 'T') || (buf[0x14] != 'P')) {
    usb_close(devh);
    return 0;
  }
      
  /* After this point we are probably dealing with an MTP device */

  /* Check if device responds to control message 2 or if there is an error*/
  ret = usb_control_msg (devh,
			 USB_ENDPOINT_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR,
			 cmd,
			 0,
			 5,
			 (char *) buf,
			 sizeof(buf),
			 1000);

  // Dump it, if requested
  if (dumpfile != NULL && ret > 0) {
    fprintf(dumpfile, "Microsoft device response to control message 2, CMD 0x%02x:\n", cmd);
    data_dump_ascii(dumpfile, buf, ret, 16);
  }
  
  /* If this is true, the device errored against control message 2 */
  if (ret == -1) {
    /* TODO: Implement callback function to let managing program know there
       was a problem, along with description of the problem */
    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
	    "ProductID:%04x encountered an error responding to "
	    "control message 2.\n"
	    "Problems may arrise but continuing\n",
	    dev->descriptor.idVendor, dev->descriptor.idProduct);
  } else if (ret <= 0x15) {
    /* TODO: Implement callback function to let managing program know there
       was a problem, along with description of the problem */
    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
	    "ProductID:%04x responded to control message 2 with a "
	    "response that was too short. Problems may arrise but "
	    "continuing\n",
	    dev->descriptor.idVendor, dev->descriptor.idProduct);
  } else if ((buf[0x12] != 'M') || (buf[0x13] != 'T') || (buf[0x14] != 'P')) {
    /* TODO: Implement callback function to let managing program know there
       was a problem, along with description of the problem */
    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
	    "ProductID:%04x encountered an error responding to "
	    "control message 2\n"
	    "Problems may arrise but continuing\n",
	    dev->descriptor.idVendor, dev->descriptor.idProduct);
  }
  
  /* Close the USB device handle */
  usb_close(devh);
  return 1;
}

/**
 * This function scans through the connected usb devices on a machine and
 * if they match known Vendor and Product identifiers appends them to the
 * dynamic array mtp_device_list. Be sure to call 
 * <code>free(mtp_device_list)</code> when you are done with it, assuming it
 * is not NULL.
 * @param mtp_device_list dynamic array of pointers to usb devices with MTP 
 *        properties (if this list is not empty, new entries will be appended
 *        to the list).
 * @return LIBMTP_ERROR_NONE implies that devices have been found, scan the list
 *        appropriately. LIBMTP_ERROR_NO_DEVICE_ATTACHED implies that no devices have
 *        been found.
 */
static LIBMTP_error_number_t get_mtp_usb_device_list(mtpdevice_list_t ** mtp_device_list)
{
  struct usb_bus *bus = init_usb();
  int found = 0;
  int i;

  for (; bus != NULL; bus = bus->next) {
    struct usb_device *dev = bus->devices;
    for (; dev != NULL; dev = dev->next) {
      if (dev->descriptor.bDeviceClass != USB_CLASS_HUB) {
        found = 0;
        for(i = 0; i < mtp_device_table_size; i++) {
          if(dev->descriptor.idVendor == mtp_device_table[i].vendor_id &&
            dev->descriptor.idProduct == mtp_device_table[i].product_id) {
            /* Append this usb device to the MTP device list */
            *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev);
            found = 1;
            break;
          }
        }
        if (!found) {
          printf("probing device descriptor\n");
          if (probe_device_descriptor(dev, NULL)) {
            /* Append this usb device to the MTP USB Device List */
            *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev);
          }
        }
	    }
    }
  }
  
  /* If nothing was found we end up here. */
  if(*mtp_device_list == NULL) {
    return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
  }
  return LIBMTP_ERROR_NONE;
}

/**
 * Detect the MTP device descriptor and return the VID and PID
 * of the first device found. This is a very low-level function
 * which is intended for use with <b>udev</b> or other hotplug
 * mechanisms. The idea is that a script may want to know if the
 * just plugged-in device was an MTP device or not.
 * 
 * @param vid the Vendor ID (VID) of the first device found.
 * @param pid the Product ID (PID) of the first device found.
 * @return the number of detected devices or -1 if the call
 *         was unsuccessful.<commit_msg>Some comments and de-prints<commit_after>/*
 * \file libusb-glue.c
 * Low-level USB interface glue towards libusb.
 *
 * Copyright (C) 2005-2007 <NAME> <<EMAIL>>
 * Copyright (C) 2005-2007 <NAME> <<EMAIL>>
 * Copyright (C) 2006-2007 <NAME>
 * Copyright (C) 2007 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Created by <NAME> on 24/12/2005. (as mtp-utils.c)
 * Modified by <NAME> 2006-03-06
 *  (Notice that Anglo-Saxons use little-endian dates and Swedes 
 *   use big-endian dates.)
 *
 */
#include "libmtp.h"
#include "libusb-glue.h"
#include "util.h"
#include "ptp.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <usb.h>

#include "ptp-pack.c"

/* To enable debug prints, switch on this */
//#define ENABLE_USB_BULK_DEBUG

/* OUR APPLICATION USB URB (2MB) ;) */
#define PTPCAM_USB_URB		2097152

/* this must not be too short - the original 4000 was not long
   enough for big file transfers. I imagine the player spends a 
   bit of time gearing up to receiving lots of data. This also makes
   connecting/disconnecting more reliable */
#define USB_TIMEOUT		10000
#define USB_CAPTURE_TIMEOUT	20000

/* USB control message data phase direction */
#ifndef USB_DP_HTD
#define USB_DP_HTD		(0x00 << 7)	/* host to device */
#endif
#ifndef USB_DP_DTH
#define USB_DP_DTH		(0x01 << 7)	/* device to host */
#endif

/* USB Feature selector HALT */
#ifndef USB_FEATURE_HALT
#define USB_FEATURE_HALT	0x00
#endif

/*
 * MTP device list, trying real bad to get all devices into
 * this list by stealing from everyone I know.
 */
static const LIBMTP_device_entry_t mtp_device_table[] = {
  
  /*
   * Creative Technology
   * Initially the Creative devices was all we supported so these are
   * the most thoroughly tested devices.
   */
  { "Creative Zen Vision", 0x041e, 0x411f, DEVICE_FLAG_NONE },
  { "Creative Portable Media Center", 0x041e, 0x4123, DEVICE_FLAG_NONE },
  { "Creative Zen Xtra (MTP mode)", 0x041e, 0x4128, DEVICE_FLAG_NONE },
  { "Second generation Dell DJ", 0x041e, 0x412f, DEVICE_FLAG_NONE },
  { "Creative Zen Micro (MTP mode)", 0x041e, 0x4130, DEVICE_FLAG_NONE },
  { "Creative Zen Touch (MTP mode)", 0x041e, 0x4131, DEVICE_FLAG_NONE },
  { "Dell Pocket DJ (MTP mode)", 0x041e, 0x4132, DEVICE_FLAG_NONE },
  { "Creative Zen Sleek (MTP mode)", 0x041e, 0x4137, DEVICE_FLAG_NONE },
  { "Creative Zen MicroPhoto", 0x041e, 0x413c, DEVICE_FLAG_NONE },
  { "Creative Zen Sleek Photo", 0x041e, 0x413d, DEVICE_FLAG_NONE },
  { "Creative Zen Vision:M", 0x041e, 0x413e, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Creative Zen V", 0x041e, 0x4150, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Creative Zen Vision:M (DVP-HD0004)", 0x041e, 0x4151, DEVICE_FLAG_NONE },
  // Reported by Darel on the XNJB forums
  { "Creative Zen V Plus", 0x041e, 0x4152, DEVICE_FLAG_NONE },
  { "Creative Zen Vision W", 0x041e, 0x4153, DEVICE_FLAG_NONE },
  // Reported by Ringofan <<EMAIL>>
  { "Creative Zen V 2GB", 0x041e, 0x4158, DEVICE_FLAG_NONE },

  /*
   * Samsung
   * We suspect that more of these are dual mode.
   */
  // From <NAME>'Neill
  { "Samsung YH-920", 0x04e8, 0x5022, DEVICE_FLAG_UNLOAD_DRIVER },
  // Contributed by aronvanammers on SourceForge
  { "Samsung YH-925GS", 0x04e8, 0x5024, DEVICE_FLAG_NONE },
  // From libgphoto2
  { "Samsung YH-820", 0x04e8, 0x502e, DEVICE_FLAG_NONE },
  // Contributed by <EMAIL>
  { "Samsung YH-925(-GS)", 0x04e8, 0x502f, DEVICE_FLAG_NONE },
  // Contributed by anonymous person on SourceForge
  { "Samsung YH-J70J", 0x04e8, 0x5033, DEVICE_FLAG_UNLOAD_DRIVER },
  // From XNJB user
  { "Samsung YP-Z5", 0x04e8, 0x503c, DEVICE_FLAG_NONE },
  // From XNJB user
  { "Samsung YP-Z5 2GB", 0x04e8, 0x5041, DEVICE_FLAG_NONE },
  // Contributed by anonymous person on SourceForge
  { "Samsung YP-T7J", 0x04e8, 0x5047, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Samsung YP-U2J (YP-U2JXB/XAA)", 0x04e8, 0x5054, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by <NAME>
  { "Samsung YP-F2J", 0x04e8, 0x5057, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by Patrick <<EMAIL>>
  { "Samsung YP-K5", 0x04e8, 0x505a, DEVICE_FLAG_NO_ZERO_READS },
  // From <EMAIL> - 0x4e8/0x507c is the UMS mode, don't add this.
  // From m.eik michalke
  { "Samsung YP-U3", 0x04e8, 0x507d, DEVICE_FLAG_NONE },
  // Reported by <NAME> <<EMAIL>>
  { "Samsung YP-T9", 0x04e8, 0x507f, DEVICE_FLAG_NONE },
  // From <NAME>
  { "Samsung YP-K3", 0x04e8, 0x5081, DEVICE_FLAG_NONE },
  // From a rouge .INF file,
  // this device ID seems to have been recycled for the Samsung SGH-A707 Cingular cellphone
  { "Samsung YH-999 Portable Media Center / Samsung SGH-A707", 0x04e8, 0x5a0f, DEVICE_FLAG_NONE },
  // From <NAME>
  { "Samsung X830 Mobile Phone", 0x04e8, 0x6702, DEVICE_FLAG_NONE },
  // From James <<EMAIL>>
  { "Samsung U600 Mobile Phone", 0x04e8, 0x6709, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * Intel
   */
  { "Intel Bandon Portable Media Center", 0x045e, 0x00c9, DEVICE_FLAG_NONE },

  /*
   * JVC
   */
  // From <NAME>
  { "JVC Alneo XA-HD500", 0x04f1, 0x6105, DEVICE_FLAG_NONE },

  /*
   * Philips
   */
  { "Philips HDD6320/00 and HDD6330/17", 0x0471, 0x014b, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Anonymous SourceForge user
  { "Philips HDD1630/17", 0x0471, 0x014c, DEVICE_FLAG_NONE },
  // from discussion forum
  { "Philips HDD085/00 and HDD082/17", 0x0471, 0x014d, DEVICE_FLAG_NONE },
  // from XNJB forum
  { "Philips GoGear SA9200", 0x0471, 0x014f, DEVICE_FLAG_NONE },
  // From <NAME>
  { "Philips GoGear Audio", 0x0471, 0x0165, DEVICE_FLAG_NONE },
  // from <NAME> <<EMAIL>>
  { "Philips Shoqbox", 0x0471, 0x0172, DEVICE_FLAG_ONLY_7BIT_FILENAMES },
  // from npedrosa
  { "Philips PSA610", 0x0471, 0x0181, DEVICE_FLAG_NONE },
  // From libgphoto2 source
  { "Philips HDD6320", 0x0471, 0x01eb, DEVICE_FLAG_NONE },
  // from XNJB user
  { "Philips PSA235", 0x0471, 0x7e01, DEVICE_FLAG_NONE },


  /*
   * SanDisk
   * several devices (c150 for sure) are definately dual-mode and must 
   * have the USB mass storage driver that hooks them unloaded first.
   * They all have problematic dual-mode making the device unload effect
   * uncertain on these devices. All except for the Linux based ones seem
   * to need DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL.
   */
  // Reported by <NAME>
  { "SanDisk Sansa m230/m240", 0x0781, 0x7400, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL},
  // Reported by <EMAIL>
  { "SanDisk Sansa c150", 0x0781, 0x7410, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // From libgphoto2 source
  // Reported by <<EMAIL>>
  // Reported by <NAME> <<EMAIL>>
  { "SanDisk Sansa e200/e250/e260/e270/e280", 0x0781, 0x7420, 
    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by XNJB user
  { "SanDisk Sansa e280", 0x0781, 0x7421, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by anonymous user at sourceforge.net
  { "SanDisk Sansa c240/c250", 0x0781, 0x7450, 
    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by XNJB user, and <NAME> <<EMAIL>>
  // This has no dual-mode so no need to unload any driver.
  // This is a Linux based device!
  { "SanDisk Sansa Connect", 0x0781, 0x7480, DEVICE_FLAG_NONE },
  // Reported by <NAME>.
  { "SanDisk Sansa Express", 0x0781, 0x7460, 
    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
  // Reported by XNJB user
  { "SanDisk Sansa m240", 0x0781, 0x7430, 
    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  

  /*
   * iRiver
   * we assume that PTP_OC_MTP_GetObjPropList is essentially
   * broken on all iRiver devices, meaning it simply won't return
   * all properties for a file when asking for metadata 0xffffffff. 
   * Please test on your device if you believe it isn't broken!
   * Some devices from http://www.mtp-ums.net/viewdeviceinfo.php
   */
  { "iRiver Portable Media Center", 0x1006, 0x4002, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver Portable Media Center", 0x1006, 0x4003, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // From an anonymous person at SourceForge
  { "iRiver iFP-880", 0x4102, 0x1008, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // From libgphoto2 source
  { "iRiver T10", 0x4102, 0x1113, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T20 FM", 0x4102, 0x1114, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // This appears at the MTP-UMS site
  { "iRiver T20", 0x4102, 0x1115, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver U10", 0x4102, 0x1116, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T10", 0x4102, 0x1117, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T20", 0x4102, 0x1118, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver T30", 0x4102, 0x1119, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME>
  { "iRiver T10 2GB", 0x4102, 0x1120, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Rough guess this is the MTP device ID...
  { "iRiver N12", 0x4102, 0x1122, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME> <<EMAIL>>
  // Newer iriver devices seem to have shaped-up firmware without any
  // of the annoying bugs.
  { "iRiver Clix2", 0x4102, 0x1126, DEVICE_FLAG_NONE },
  // Reported by <NAME>
  { "iRiver Clix", 0x4102, 0x112a, 
    DEVICE_FLAG_NO_ZERO_READS | DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME> <<EMAIL>>
  { "iRiver X20", 0x4102, 0x1132, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME> <<EMAIL>>
  { "iRiver T60", 0x4102, 0x1134, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  // Reported by <NAME>
  { "iRiver H10 20GB", 0x4102, 0x2101, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
  { "iRiver H10", 0x4102, 0x2102, 
    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },


  /*
   * Dell
   */
  { "Dell DJ Itty", 0x413c, 0x4500, DEVICE_FLAG_NONE },
  
  /*
   * Toshiba
   */
  { "Toshiba Gigabeat MEGF-40", 0x0930, 0x0009, DEVICE_FLAG_NONE },
  { "Toshiba Gigabeat", 0x0930, 0x000c, DEVICE_FLAG_NONE },
  // Reported by <NAME>
  { "Toshiba Gigabeat P20", 0x0930, 0x000f, DEVICE_FLAG_NONE },
  // From libgphoto2
  { "Toshiba Gigabeat S", 0x0930, 0x0010, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
  // Reported by <NAME>
  { "Toshiba Gigabeat P10", 0x0930, 0x0011, DEVICE_FLAG_NONE },
  // Reported by <NAME> <<EMAIL>>
  { "Toshiba Gigabeat U", 0x0930, 0x0016, DEVICE_FLAG_NONE },
  
  /*
   * Archos
   * These devices have some dual-mode interfaces which will really
   * respect the driver unloading, so DEVICE_FLAG_UNLOAD_DRIVER
   * really work on these devices!
   */
  // Reported by <NAME> <<EMAIL>>
  { "Archos Gmini XS100", 0x0e79, 0x1207, DEVICE_FLAG_UNLOAD_DRIVER },
  // Added by <NAME>
  { "Archos XS202 (MTP mode)", 0x0e79, 0x1208, DEVICE_FLAG_NONE },
  // Reported by <EMAIL>
  { "Archos 104 (MTP mode)", 0x0e79, 0x120a, DEVICE_FLAG_NONE },
  // Reported by <NAME> <<EMAIL>>
  { "Archos 504 (MTP mode)", 0x0e79, 0x1307, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by <NAME> <<EMAIL>>
  { "Archos 704 mobile dvr", 0x0e79, 0x130d, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * Dunlop (OEM of EGOMAN ltd?) reported by Nanomad
   * This unit is falsely detected as USB mass storage in Linux
   * prior to kernel 2.6.19 (fixed by patch from Alan Stern)
   * so on older kernels special care is needed to remove the
   * USB mass storage driver that erroneously binds to the device
   * interface.
   */
  { "Dunlop MP3 player 1GB / EGOMAN MD223AFD", 0x10d6, 0x2200, DEVICE_FLAG_UNLOAD_DRIVER},
  
  /*
   * Microsoft
   */
  // Reported by <NAME>
  { "Microsoft Zune", 0x045e, 0x0710, DEVICE_FLAG_NONE }, 
  
  /*
   * Sirius
   */
  { "Sirius Stiletto", 0x18f6, 0x0102, DEVICE_FLAG_NONE },

  /*
   * Canon
   * This is actually a camera, but it has a Microsoft device descriptor
   * and reports itself as supporting the MTP extension.
   */
  {"Canon PowerShot A640 (PTP/MTP mode)", 0x04a9, 0x3139, DEVICE_FLAG_NONE },

  /*
   * Nokia
   */
  // From: <NAME> <<EMAIL>>
  {"Nokia 5300 Mobile Phone", 0x0421, 0x04ba, DEVICE_FLAG_NONE },
  // From <NAME> <<EMAIL>>
  {"Nokia N73 Mobile Phone", 0x0421, 0x04d1, DEVICE_FLAG_UNLOAD_DRIVER },
  // From Swapan <<EMAIL>>
  {"Nokia N75 Mobile Phone", 0x0421, 0x04e1, DEVICE_FLAG_NONE },
  // From: <NAME> <<EMAIL>>
  {"Nokia N80 Internet Edition (Media Player)", 0x0421, 0x04f1, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * LOGIK
   * Sold in the UK, seem to be manufactured by CCTech in China.
   */
  {"Logik LOG DAX MP3 and DAB Player", 0x13d1, 0x7002, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * RCA / Thomson
   */
  {"Thomson Opal / RCA Lyra MC4002", 0x069b, 0x0777, DEVICE_FLAG_NONE },
  
  /*
   * NTT DoCoMo
   */
  {"FOMA F903iX HIGH-SPEED", 0x04c5, 0x1140, DEVICE_FLAG_NONE },

  /*
   * Palm device userland program named Pocket Tunes
   * Reported by <NAME> <<EMAIL>>
   */
  {"Palm / Handspring Pocket Tunes", 0x1703, 0x0001, DEVICE_FLAG_NONE },
  // Reported by anonymous submission
  {"Palm / Handspring Pocket Tunes 4", 0x1703, 0x0002, DEVICE_FLAG_NONE },

  /*
   * TrekStor devices
   * Their datasheet claims their devices are dualmode so probably needs to
   * unload the attached drivers here.
   */
  // Reported by <NAME> <<EMAIL>>
  {"TrekStor Vibez i.Beat sweez FM", 0x0402, 0x0611, DEVICE_FLAG_UNLOAD_DRIVER },
  // Reported by <NAME> <<EMAIL>>
  {"TrekStor Vibez 8/12GB", 0x066f, 0x842a, DEVICE_FLAG_UNLOAD_DRIVER },
  
  /*
   * Disney (have had no reports of this actually working.)
   */
  // Reported by XNJB user
  {"Disney MixMax", 0x0aa6, 0x6021, DEVICE_FLAG_NONE },

  /*
   * Cowon Systems, Inc.
   * The iAudio audiophile devices don't encourage the use of MTP.
   */
  // Reported by <NAME> <<EMAIL>>
  {"Cowon iAudio U3 (MTP mode)", 0x0e21, 0x0701, DEVICE_FLAG_NONE },
  // Reported by <NAME>
  {"Cowon iAudio 7 (MTP mode)", 0x0e21, 0x0751, DEVICE_FLAG_NONE },
  // Reported by <NAME> <<EMAIL>>
  {"Cowon iAudio D2 (MTP mode)", 0x0e21, 0x0801, 
   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },

  /*
   * Insignia, dual-mode.
   */
  { "Insignia NS-DV45", 0x19ff, 0x0303, DEVICE_FLAG_UNLOAD_DRIVER },

  /*
   * LG Electronics
   */
  // Not verified - anonymous submission
  { "LG UP3", 0x043e, 0x70b1, DEVICE_FLAG_NONE },
  
  /*
   * Other strange stuff.
   */
  {"Isabella's prototype", 0x0b20, 0xddee, DEVICE_FLAG_NONE }
};
static const int mtp_device_table_size = sizeof(mtp_device_table) / sizeof(LIBMTP_device_entry_t);

int ptpcam_usb_timeout = USB_TIMEOUT;

// Local functions
static struct usb_bus* init_usb();
static void close_usb(PTP_USB* ptp_usb);
static void find_interface_and_endpoints(struct usb_device *dev,
					 uint8_t *interface,
					 int* inep, 
					 int* inep_maxpacket, 
					 int* outep, 
					 int* outep_maxpacket, 
					 int* intep);
static void clear_stall(PTP_USB* ptp_usb);
static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev);
static short ptp_write_func (unsigned long,PTPDataHandler*,void *data,unsigned long*);
static short ptp_read_func (unsigned long,PTPDataHandler*,void *data,unsigned long*,int);
static int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep);
static int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status);

/**
 * Get a list of the supported USB devices.
 *
 * The developers depend on users of this library to constantly
 * add in to the list of supported devices. What we need is the
 * device name, USB Vendor ID (VID) and USB Product ID (PID).
 * put this into a bug ticket at the project homepage, please.
 * The VID/PID is used to let e.g. udev lift the device to
 * console userspace access when it's plugged in.
 *
 * @param devices a pointer to a pointer that will hold a device
 *        list after the call to this function, if it was
 *        successful.
 * @param numdevs a pointer to an integer that will hold the number
 *        of devices in the device list if the call was successful.
 * @return 0 if the list was successfull retrieved, any other
 *        value means failure.
 */
int LIBMTP_Get_Supported_Devices_List(LIBMTP_device_entry_t ** const devices, int * const numdevs)
{
  *devices = (LIBMTP_device_entry_t *) &mtp_device_table;
  *numdevs = mtp_device_table_size;
  return 0;
}


static struct usb_bus* init_usb()
{
  usb_init();
  usb_find_busses();
  usb_find_devices();
  return (usb_get_busses());
}

/**
 * Small recursive function to append a new usb_device to the linked list of
 * USB MTP devices
 * @param devlist dynamic linked list of pointers to usb devices with MTP 
 * properties.
 * @param next New USB MTP device to be added to list
 * @return nothing
 */
static mtpdevice_list_t *append_to_mtpdevice_list(mtpdevice_list_t *devlist,
				     struct usb_device *newdevice)
{
  mtpdevice_list_t *new_list_entry;
  
  new_list_entry = (mtpdevice_list_t *) malloc(sizeof(mtpdevice_list_t));
  if (new_list_entry == NULL) {
    return NULL;
  }
  // Fill in USB device, if we *HAVE* to make a copy of the device do it here.
  new_list_entry->libusb_device = newdevice;
  new_list_entry->next = NULL;
  
  if (devlist == NULL) {
    return new_list_entry;
  } else {
    mtpdevice_list_t *tmp = devlist;
    while (tmp->next != NULL) {
      tmp = tmp->next;
    }
    tmp->next = new_list_entry;
  }
  return devlist;
}

/**
 * Small recursive function to free dynamic memory allocated to the linked list
 * of USB MTP devices
 * @param devlist dynamic linked list of pointers to usb devices with MTP 
 * properties.
 * @return nothing
 */
void free_mtpdevice_list(mtpdevice_list_t *devlist)
{
  mtpdevice_list_t *tmplist = devlist;

  if (devlist == NULL)
    return;
  while (tmplist != NULL) {
    mtpdevice_list_t *tmp = tmplist;
    tmplist = tmplist->next;
    // Do not free() the fields (ptp_usb, parms)! These are used elsewhere.
    free(tmp);
  }
  return;
}

/**
 * This checks if a device has an MTP descriptor. The descriptor was
 * elaborated about in gPhoto bug 1482084, and some official documentation
 * with no strings attached was published by Microsoft at
 * http://www.microsoft.com/whdc/system/bus/USB/USBFAQ_intermed.mspx#E3HAC
 *
 * @param dev a device struct from libusb.
 * @param dumpfile set to non-NULL to make the descriptors dump out
 *        to this file in human-readable hex so we can scruitinze them.
 * @return 1 if the device is MTP compliant, 0 if not.
 */
static int probe_device_descriptor(struct usb_device *dev, FILE *dumpfile)
{
  usb_dev_handle *devh;
  unsigned char buf[1024], cmd;
  int ret;
  
  /* Don't examine hubs (no point in that) */
  if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) {
    return 0;
  }
  
  /* Attempt to open Device on this port */
  devh = usb_open(dev);
  if (devh == NULL) {
    /* Could not open this device */
    return 0;
  }
  
  /* Read the special descriptor */
  ret = usb_get_descriptor(devh, 0x03, 0xee, buf, sizeof(buf));

  // Dump it, if requested
  if (dumpfile != NULL && ret > 0) {
    fprintf(dumpfile, "Microsoft device descriptor 0xee:\n");
    data_dump_ascii(dumpfile, buf, ret, 16);
  }
  
  /* Check if descriptor length is at least 10 bytes */
  if (ret < 10) {
    usb_close(devh);
    return 0;
  }
      
  /* Check if this device has a Microsoft Descriptor */
  if (!((buf[2] == 'M') && (buf[4] == 'S') &&
	(buf[6] == 'F') && (buf[8] == 'T'))) {
    usb_close(devh);
    return 0;
  }
      
  /* Check if device responds to control message 1 or if there is an error */
  cmd = buf[16];
  ret = usb_control_msg (devh,
			 USB_ENDPOINT_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR,
			 cmd,
			 0,
			 4,
			 (char *) buf,
			 sizeof(buf),
			 1000);

  // Dump it, if requested
  if (dumpfile != NULL && ret > 0) {
    fprintf(dumpfile, "Microsoft device response to control message 1, CMD 0x%02x:\n", cmd);
    data_dump_ascii(dumpfile, buf, ret, 16);
  }
  
  /* If this is true, the device either isn't MTP or there was an error */
  if (ret <= 0x15) {
    /* TODO: If there was an error, flag it and let the user know somehow */
    /* if(ret == -1) {} */
    usb_close(devh);
    return 0;
  }
  
  /* Check if device is MTP or if it is something like a USB Mass Storage 
     device with Janus DRM support */
  if ((buf[0x12] != 'M') || (buf[0x13] != 'T') || (buf[0x14] != 'P')) {
    usb_close(devh);
    return 0;
  }
      
  /* After this point we are probably dealing with an MTP device */

  /* Check if device responds to control message 2 or if there is an error*/
  ret = usb_control_msg (devh,
			 USB_ENDPOINT_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR,
			 cmd,
			 0,
			 5,
			 (char *) buf,
			 sizeof(buf),
			 1000);

  // Dump it, if requested
  if (dumpfile != NULL && ret > 0) {
    fprintf(dumpfile, "Microsoft device response to control message 2, CMD 0x%02x:\n", cmd);
    data_dump_ascii(dumpfile, buf, ret, 16);
  }
  
  /* If this is true, the device errored against control message 2 */
  if (ret == -1) {
    /* TODO: Implement callback function to let managing program know there
       was a problem, along with description of the problem */
    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
	    "ProductID:%04x encountered an error responding to "
	    "control message 2.\n"
	    "Problems may arrise but continuing\n",
	    dev->descriptor.idVendor, dev->descriptor.idProduct);
  } else if (ret <= 0x15) {
    /* TODO: Implement callback function to let managing program know there
       was a problem, along with description of the problem */
    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
	    "ProductID:%04x responded to control message 2 with a "
	    "response that was too short. Problems may arrise but "
	    "continuing\n",
	    dev->descriptor.idVendor, dev->descriptor.idProduct);
  } else if ((buf[0x12] != 'M') || (buf[0x13] != 'T') || (buf[0x14] != 'P')) {
    /* TODO: Implement callback function to let managing program know there
       was a problem, along with description of the problem */
    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
	    "ProductID:%04x encountered an error responding to "
	    "control message 2\n"
	    "Problems may arrise but continuing\n",
	    dev->descriptor.idVendor, dev->descriptor.idProduct);
  }
  
  /* Close the USB device handle */
  usb_close(devh);
  return 1;
}

/**
 * This function scans through the connected usb devices on a machine and
 * if they match known Vendor and Product identifiers appends them to the
 * dynamic array mtp_device_list. Be sure to call 
 * <code>free(mtp_device_list)</code> when you are done with it, assuming it
 * is not NULL.
 * @param mtp_device_list dynamic array of pointers to usb devices with MTP 
 *        properties (if this list is not empty, new entries will be appended
 *        to the list).
 * @return LIBMTP_ERROR_NONE implies that devices have been found, scan the list
 *        appropriately. LIBMTP_ERROR_NO_DEVICE_ATTACHED implies that no 
 *        devices have been found.
 */
static LIBMTP_error_number_t get_mtp_usb_device_list(mtpdevice_list_t ** mtp_device_list)
{
  struct usb_bus *bus = init_usb();
  for (; bus != NULL; bus = bus->next) {
    struct usb_device *dev = bus->devices;
    for (; dev != NULL; dev = dev->next) {
      if (dev->descriptor.bDeviceClass != USB_CLASS_HUB) {
	int i;
        int found = 0;

	// First check if we know about the device already.
	// Devices well known to us will not have their descriptors
	// probed, it caused problems with some devices.
        for(i = 0; i < mtp_device_table_size; i++) {
          if(dev->descriptor.idVendor == mtp_device_table[i].vendor_id &&
            dev->descriptor.idProduct == mtp_device_table[i].product_id) {
            /* Append this usb device to the MTP device list */
            *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev);
            found = 1;
            break;
          }
        }
	// If we didn't know it, try probing the "OS Descriptor".
        if (!found) {
          if (probe_device_descriptor(dev, NULL)) {
            /* Append this usb device to the MTP USB Device List */
            *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev);
          }
        }
      }
    }
  }
  
  /* If nothing was found we end up here. */
  if(*mtp_device_list == NULL) {
    return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
  }
  return LIBMTP_ERROR_NONE;
}

/**
 * Detect the MTP device descriptor and return the VID and PID
 * of the first device found. This is a very low-level function
 * which is intended for use with <b>udev</b> or other hotplug
 * mechanisms. The idea is that a script may want to know if the
 * just plugged-in device was an MTP device or not.
 * 
 * @param vid the Vendor ID (VID) of the first device found.
 * @param pid the Product ID (PID) of the first device found.
 * @return the number of detected devices or -1 if the call
 *         was unsuccessful.
{'fraction_non_alphanumeric': 0.10431162206260197, 'fraction_numerical': 0.052230828379062656, 'mean_word_length': 3.487250694269124, 'pattern_counts': {'":': 0, '<': 237, '<?xml version=': 0, '>': 265, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5241323', 'n_tokens_mistral': 22037, 'n_tokens_neox': 19433, 'n_words': 5606}