libusb-glue.c

来自「Media transfer protocol implementation o」· C语言 代码 · 共 864 行 · 第 1/2 页

C
864
字号
    if (result > 0) {    *readbytes = curread;    return (PTP_RC_OK);  } else {    return PTP_ERROR_IO;  }}// Based on same function on library.c in libgphoto2static shortptp_write_func (unsigned char *bytes, unsigned int size, void *data){  PTP_USB *ptp_usb = (PTP_USB *)data;  int towrite = 0;  int result = 0;  int curwrite = 0;    /*   * gp_port_write returns (in case of success) the number of bytes   * written. Too large blocks (>5x MB) could timeout.   */  while (curwrite < size) {    towrite = size-curwrite;    if (towrite > CONTEXT_BLOCK_SIZE)      towrite = CONTEXT_BLOCK_SIZE;    result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)(bytes+curwrite),towrite,ptpcam_usb_timeout);#ifdef ENABLE_USB_BULK_DEBUG    printf("USB OUT==>\n");    data_dump_ascii (stdout,(bytes+curwrite),towrite,16);#endif    if (result < 0)      return PTP_ERROR_IO;    curwrite += result;    if (result < towrite) /* short writes happen */      break;  }  // Increase counters, call callback  if (ptp_usb->callback_active) {    ptp_usb->current_transfer_complete += curwrite;    if (ptp_usb->current_transfer_complete > ptp_usb->current_transfer_total) {      // Fishy... but some commands have unpredictable lengths.      // send last update and disable callback.      ptp_usb->current_transfer_complete = ptp_usb->current_transfer_total;      ptp_usb->callback_active = 0;    }    if (ptp_usb->current_transfer_callback != NULL) {      (void) ptp_usb->current_transfer_callback(ptp_usb->current_transfer_complete,						ptp_usb->current_transfer_total,						ptp_usb->current_transfer_callback_data);    }  }    // If this is the last transfer (callbacks only active if this function called repeatedly with  // new data, otherwise this is a single large transaction which ends here).  // a request and the bulk send header.  if (!ptp_usb->callback_active) {    // Then terminate an even packet boundary write with a zero length packet    if ((size % ptp_usb->outep_maxpacket) == 0) {      result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)"x",0,ptpcam_usb_timeout);    }  } else if (ptp_usb->current_transfer_complete == ptp_usb->current_transfer_total) {    // This is the amount actually transfered in this large transaction.    uint64_t actual_xfer_size = ptp_usb->current_transfer_total - 2*PTP_USB_BULK_HDR_LEN;        if ((actual_xfer_size % ptp_usb->outep_maxpacket) == 0) {      result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)"x",0,ptpcam_usb_timeout);    }    // Set as complete and disable callback, just as good.     // This also blocks callbacks from the following response command.    if (ptp_usb->current_transfer_callback != NULL) {      (void) ptp_usb->current_transfer_callback(ptp_usb->current_transfer_total, // Both total = 100%						ptp_usb->current_transfer_total, // Both total = 100%						ptp_usb->current_transfer_callback_data);    }    ptp_usb->callback_active = 0;  }  if (result < 0)    return PTP_ERROR_IO;  return PTP_RC_OK;}// This is a bit hackish at the moment. I wonder if it even works.static short ptp_check_int (unsigned char *bytes, unsigned int size, void *data, unsigned int *rlen){  PTP_USB *ptp_usb = (PTP_USB *)data;  int result;    result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)bytes,size,ptpcam_usb_timeout);  if (result==0)    result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) bytes, size, ptpcam_usb_timeout);  if (result >= 0) {    *rlen = result;    return (PTP_RC_OK);  } else {    return PTP_ERROR_IO;  }}static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev){  usb_dev_handle *device_handle;    params->write_func=ptp_write_func;  params->read_func=ptp_read_func;  params->check_int_func=ptp_check_int;  params->check_int_fast_func=ptp_check_int;  params->error_func=NULL;  params->debug_func=NULL;  params->sendreq_func=ptp_usb_sendreq;  params->senddata_func=ptp_usb_senddata;  params->getresp_func=ptp_usb_getresp;  params->getdata_func=ptp_usb_getdata;  params->data=ptp_usb;  params->transaction_id=0;  params->byteorder = PTP_DL_LE;    if ((device_handle = usb_open(dev))){    if (!device_handle) {      perror("usb_open()");      return -1;    }    ptp_usb->handle = device_handle;#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP    /*     * If this device is known to be wrongfully claimed by other kernel     * drivers (such as mass storage), then try to unload it to make it     * accessible from user space.     */    if (ptp_usb->device_flags & DEVICE_FLAG_UNLOAD_DRIVER) {      if (usb_detach_kernel_driver_np(device_handle, dev->config->interface->altsetting->bInterfaceNumber)) {	// Totally ignore this error!	// perror("usb_detach_kernel_driver_np()");      }    }#endif#ifdef __WIN32__    // Only needed on Windows    if (usb_set_configuration(device_handle, dev->config->bConfigurationValue)) {      perror("usb_set_configuration()");      return -1;    }#endif    if (usb_claim_interface(device_handle, dev->config->interface->altsetting->bInterfaceNumber)) {      perror("usb_claim_interface()");      return -1;    }    ptp_usb->interface = dev->config->interface->altsetting->bInterfaceNumber;  }  return 0;}static void clear_stall(PTP_USB* ptp_usb){  uint16_t status=0;  int ret;    /* check the inep status */  ret = usb_get_endpoint_status(ptp_usb,ptp_usb->inep,&status);  if (ret<0) perror ("inep: usb_get_endpoint_status()");  /* and clear the HALT condition if happend */  else if (status) {    printf("Resetting input pipe!\n");    ret=usb_clear_stall_feature(ptp_usb,ptp_usb->inep);    /*usb_clear_halt(ptp_usb->handle,ptp_usb->inep); */    if (ret<0)perror ("usb_clear_stall_feature()");  }  status=0;    /* check the outep status */  ret=usb_get_endpoint_status(ptp_usb,ptp_usb->outep,&status);  if (ret<0) perror ("outep: usb_get_endpoint_status()");  /* and clear the HALT condition if happend */  else if (status) {    printf("Resetting output pipe!\n");    ret=usb_clear_stall_feature(ptp_usb,ptp_usb->outep);    /*usb_clear_halt(ptp_usb->handle,ptp_usb->outep); */    if (ret<0)perror ("usb_clear_stall_feature()");  }    /*usb_clear_halt(ptp_usb->handle,ptp_usb->intep); */}static void close_usb(PTP_USB* ptp_usb, uint8_t interfaceNumber){  clear_stall(ptp_usb);  // Added to clear some stuff on the OUT endpoint  // TODO: is this good on the Mac too?  usb_resetep(ptp_usb->handle, ptp_usb->outep);  usb_release_interface(ptp_usb->handle, interfaceNumber);  // Brutally reset device  // TODO: is this good on the Mac too?  usb_reset(ptp_usb->handle);  usb_close(ptp_usb->handle);}/* find_device() returns the pointer to a usb_device structure matching given busn, devicen numbers. If any or both of arguments are 0 then the first matching PTP device structure is returned.  */static struct usb_device* find_device (int busn, int devn, short force){  struct usb_bus *bus;  struct usb_device *dev;    bus=init_usb();  for (; bus; bus = bus->next)    for (dev = bus->devices; dev; dev = dev->next)      /* somtimes dev->config is null, not sure why... */      if (dev->config != NULL)	if (dev->descriptor.bDeviceClass!=USB_CLASS_HUB)	  {	    int curbusn, curdevn;	    	    curbusn=strtol(bus->dirname,NULL,10);	    curdevn=strtol(dev->filename,NULL,10);	    	    if (devn==0) {	      if (busn==0) return dev;	      if (curbusn==busn) return dev;	    } else {	      if ((busn==0)&&(curdevn==devn)) return dev;	      if ((curbusn==busn)&&(curdevn==devn)) return dev;	    }	  }  return NULL;}/** * This function scans through the device list to see if there are * some devices to connect to. The table at the top of this file is * used to identify potential devices. */uint16_t connect_first_device(PTPParams *params, PTP_USB *ptp_usb, uint8_t *interfaceNumber){  struct usb_bus *bus;  struct usb_device *dev;  struct usb_endpoint_descriptor *ep;  PTPDeviceInfo deviceinfo;  uint16_t ret=0;  int n;  // Reset device flags  ptp_usb->device_flags = DEVICE_FLAG_NONE;  // First try to locate the device using the extended  // device descriptor.  dev = probe_usb_bus_for_mtp_devices();  if (dev != NULL) {    int i;    // See if we can find the name of this beast    for (i = 0; i < mtp_device_table_size; i++) {      LIBMTP_device_entry_t const *mtp_device = &mtp_device_table[i];      if (dev->descriptor.idVendor == mtp_device->vendor_id &&	  dev->descriptor.idProduct == mtp_device->product_id ) {	printf("Autodetected device \"%s\" (VID=%04x,PID=%04x) is known.\n", 	       mtp_device->name, dev->descriptor.idVendor, dev->descriptor.idProduct);	ptp_usb->device_flags = mtp_device->device_flags;	break;      }    }    if (i == mtp_device_table_size) {      printf("Autodetected device with VID=%04x and PID=%04x is UNKNOWN.\n", 	     dev->descriptor.idVendor, dev->descriptor.idProduct);      printf("Please report this VID/PID and the device model name etc to the\n");      printf("libmtp development team!\n");    }  }  // If autodetection fails, scan the bus for well known devices.  if (dev == NULL) {    bus = init_usb();    for (; bus; bus = bus->next) {      for (dev = bus->devices; dev; dev = dev->next) {	int i;		// Loop over the list of supported devices	for (i = 0; i < mtp_device_table_size; i++) {	  LIBMTP_device_entry_t const *mtp_device = &mtp_device_table[i];	  	  if (dev->descriptor.bDeviceClass != USB_CLASS_HUB && 	      dev->descriptor.idVendor == mtp_device->vendor_id &&	      dev->descriptor.idProduct == mtp_device->product_id ) {	    	    printf("Found non-autodetected device \"%s\" on USB bus...\n", mtp_device->name);	    ptp_usb->device_flags = mtp_device->device_flags;            goto next_step;	    	  }	}      }    }  }next_step:  // Still not found any?  if (dev == NULL) {    return PTP_CD_RC_NO_DEVICES;  }  // Found a device, then assign endpoints to ptp_usb...  ep = dev->config->interface->altsetting->endpoint;  n = dev->config->interface->altsetting->bNumEndpoints;  find_endpoints(dev, &ptp_usb->inep, &ptp_usb->inep_maxpacket,		 &ptp_usb->outep, &ptp_usb->outep_maxpacket, &ptp_usb->intep);    // printf("Init PTP USB...\n");  if (init_ptp_usb(params, ptp_usb, dev) < 0) {    return PTP_CD_RC_ERROR_CONNECTING;  }    ret = ptp_opensession(params,1);  // This works in situations where previous bad applications have not used LIBMTP_Release_Device on exit  if (ret == PTP_ERROR_IO) {	printf("%s\n","PTP ERROR IO: Trying again after resetting USB");        // printf("%s\n","Closing USB interface...");	close_usb(ptp_usb,dev->config->interface->altsetting->bInterfaceNumber);        // printf("%s\n","Init PTP USB...");	if (init_ptp_usb(params, ptp_usb, dev) < 0) { 	   return PTP_CD_RC_ERROR_CONNECTING;  	}		ret = ptp_opensession(params,1);  }  // printf("Session open (%d)...\n", ret);  if (ret == PTP_RC_InvalidTransactionID) {    params->transaction_id += 10;    ret = ptp_opensession(params,1);  }  if (ret != PTP_RC_SessionAlreadyOpened && ret != PTP_RC_OK) {    printf("Could not open session! (Return code %d)\n  Try to reset the device.\n", ret);    usb_release_interface(ptp_usb->handle,dev->config->interface->altsetting->bInterfaceNumber);  }  // It is actually permissible to call this before opening the session  ret = ptp_getdeviceinfo(params, &deviceinfo);  if (ret != PTP_RC_OK) {    printf("Could not get device info!\n");    usb_release_interface(ptp_usb->handle,dev->config->interface->altsetting->bInterfaceNumber);    return PTP_CD_RC_ERROR_CONNECTING;  }    // we're connected, return OK  *interfaceNumber = dev->config->interface->altsetting->bInterfaceNumber;    return PTP_CD_RC_CONNECTED;}static void find_endpoints(struct usb_device *dev, int* inep, int* inep_maxpacket, int* outep, int *outep_maxpacket, int* intep){  int i,n;  struct usb_endpoint_descriptor *ep;    ep = dev->config->interface->altsetting->endpoint;  n=dev->config->interface->altsetting->bNumEndpoints;    for (i=0;i<n;i++) {    if (ep[i].bmAttributes==USB_ENDPOINT_TYPE_BULK)	{      if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==	  USB_ENDPOINT_DIR_MASK)	{	  *inep=ep[i].bEndpointAddress;	  *inep_maxpacket=ep[i].wMaxPacketSize;	}      if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==0)	{	  *outep=ep[i].bEndpointAddress;	  *outep_maxpacket=ep[i].wMaxPacketSize;	}    } else if (ep[i].bmAttributes==USB_ENDPOINT_TYPE_INTERRUPT){      if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==	  USB_ENDPOINT_DIR_MASK)	{	  *intep=ep[i].bEndpointAddress;	}    }  }}int open_device (int busn, int devn, short force, PTP_USB *ptp_usb, PTPParams *params, struct usb_device **dev){#ifdef DEBUG  printf("dev %i\tbus %i\n",devn,busn);#endif    *dev=find_device(busn,devn,force);  if (*dev==NULL) {    fprintf(stderr,"could not find any device matching given "	    "bus/dev numbers\n");    exit(-1);  }  find_endpoints(*dev,&ptp_usb->inep,&ptp_usb->inep_maxpacket,&ptp_usb->outep,&ptp_usb->outep_maxpacket,&ptp_usb->intep);    if (init_ptp_usb(params, ptp_usb, *dev) < 0) {    return -1;  }  if (ptp_opensession(params,1)!=PTP_RC_OK) {    fprintf(stderr,"ERROR: Could not open session!\n");    close_usb(ptp_usb, (*dev)->config->interface->altsetting->bInterfaceNumber);    return -1;  }  return 0;}void close_device (PTP_USB *ptp_usb, PTPParams *params, uint8_t interfaceNumber){  if (ptp_closesession(params)!=PTP_RC_OK)    fprintf(stderr,"ERROR: Could not close session!\n");  close_usb(ptp_usb, interfaceNumber);}static int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep){    return (usb_control_msg(ptp_usb->handle,			  USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE, USB_FEATURE_HALT,			  ep, NULL, 0, 3000));}static int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status){  return (usb_control_msg(ptp_usb->handle,			  USB_DP_DTH|USB_RECIP_ENDPOINT, USB_REQ_GET_STATUS,			  USB_FEATURE_HALT, ep, (char *)status, 2, 3000));}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?