mirror of
				https://github.com/Klipper3d/klipper.git
				synced 2025-10-31 02:15:52 +01:00 
			
		
		
		
	
		
			
	
	
		
			413 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			413 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  |  * Copyright (C) 2006 Free Software Initiative of Japan | ||
|  |  * | ||
|  |  * Author: NIIBE Yutaka  <gniibe at fsij.org> | ||
|  |  * | ||
|  |  * This file can be distributed under the terms and conditions of the | ||
|  |  * GNU General Public License version 2 (or later). | ||
|  |  * | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <errno.h>
 | ||
|  | #include <usb.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | #define USB_RT_HUB			(USB_TYPE_CLASS | USB_RECIP_DEVICE)
 | ||
|  | #define USB_RT_PORT			(USB_TYPE_CLASS | USB_RECIP_OTHER)
 | ||
|  | #define USB_PORT_FEAT_POWER		8
 | ||
|  | #define USB_PORT_FEAT_INDICATOR         22
 | ||
|  | #define USB_DIR_IN			0x80		/* to host */
 | ||
|  | 
 | ||
|  | #define COMMAND_SET_NONE  0
 | ||
|  | #define COMMAND_SET_LED   1
 | ||
|  | #define COMMAND_SET_POWER 2
 | ||
|  | #define HUB_LED_GREEN 2
 | ||
|  | 
 | ||
|  | static void | ||
|  | usage (const char *progname) | ||
|  | { | ||
|  |   fprintf (stderr, | ||
|  | 	   "Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}] \\\n" | ||
|  | 	   "          [-P PORT] [{-p [VALUE]|-l [VALUE]}]\n", progname); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | exit_with_usage (const char *progname) | ||
|  | { | ||
|  |   usage (progname); | ||
|  |   exit (1); | ||
|  | } | ||
|  | 
 | ||
|  | #define HUB_CHAR_LPSM		0x0003
 | ||
|  | #define HUB_CHAR_PORTIND        0x0080
 | ||
|  | 
 | ||
|  | struct usb_hub_descriptor { | ||
|  |   unsigned char bDescLength; | ||
|  |   unsigned char bDescriptorType; | ||
|  |   unsigned char bNbrPorts; | ||
|  |   unsigned char wHubCharacteristics[2]; | ||
|  |   unsigned char bPwrOn2PwrGood; | ||
|  |   unsigned char bHubContrCurrent; | ||
|  |   unsigned char data[0]; | ||
|  | }; | ||
|  | 
 | ||
|  | #define CTRL_TIMEOUT 1000
 | ||
|  | #define USB_STATUS_SIZE 4
 | ||
|  | 
 | ||
|  | #define MAX_HUBS 128
 | ||
|  | struct hub_info { | ||
|  |   int busnum, devnum; | ||
|  |   struct usb_device *dev; | ||
|  |   int nport; | ||
|  |   int indicator_support; | ||
|  | }; | ||
|  | 
 | ||
|  | static struct hub_info hubs[MAX_HUBS]; | ||
|  | static int number_of_hubs_with_feature; | ||
|  | 
 | ||
|  | static void | ||
|  | hub_port_status (usb_dev_handle *uh, int nport) | ||
|  | { | ||
|  |   int i; | ||
|  | 
 | ||
|  |   printf(" Hub Port Status:\n"); | ||
|  |   for (i = 0; i < nport; i++) | ||
|  |     { | ||
|  |       char buf[USB_STATUS_SIZE]; | ||
|  |       int ret; | ||
|  | 
 | ||
|  |       ret = usb_control_msg (uh, | ||
|  | 			     USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_OTHER, | ||
|  | 			     USB_REQ_GET_STATUS,  | ||
|  | 			     0, i + 1, | ||
|  | 			     buf, USB_STATUS_SIZE, | ||
|  | 			     CTRL_TIMEOUT); | ||
|  |       if (ret < 0) | ||
|  | 	{ | ||
|  | 	  fprintf (stderr, | ||
|  | 		   "cannot read port %d status, %s (%d)\n", | ||
|  | 		   i + 1, strerror(errno), errno); | ||
|  | 	  break; | ||
|  | 	} | ||
|  | 
 | ||
|  |       printf("   Port %d: %02x%02x.%02x%02x", i + 1, | ||
|  | 	     buf[3], buf [2], | ||
|  | 	     buf[1], buf [0]); | ||
|  | 
 | ||
|  |       printf("%s%s%s%s%s", | ||
|  | 	     (buf[2] & 0x10) ? " C_RESET" : "", | ||
|  | 	     (buf[2] & 0x08) ? " C_OC" : "", | ||
|  | 	     (buf[2] & 0x04) ? " C_SUSPEND" : "", | ||
|  | 	     (buf[2] & 0x02) ? " C_ENABLE" : "", | ||
|  | 	     (buf[2] & 0x01) ? " C_CONNECT" : ""); | ||
|  | 
 | ||
|  |       printf("%s%s%s%s%s%s%s%s%s%s\n", | ||
|  | 	     (buf[1] & 0x10) ? " indicator" : "", | ||
|  | 	     (buf[1] & 0x08) ? " test" : "", | ||
|  | 	     (buf[1] & 0x04) ? " highspeed" : "", | ||
|  | 	     (buf[1] & 0x02) ? " lowspeed" : "", | ||
|  | 	     (buf[1] & 0x01) ? " power" : "", | ||
|  | 	     (buf[0] & 0x10) ? " RESET" : "", | ||
|  | 	     (buf[0] & 0x08) ? " oc" : "", | ||
|  | 	     (buf[0] & 0x04) ? " suspend" : "", | ||
|  | 	     (buf[0] & 0x02) ? " enable" : "", | ||
|  | 	     (buf[0] & 0x01) ? " connect" : ""); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static int | ||
|  | usb_find_hubs (int listing, int verbose, int busnum, int devnum, int hub) | ||
|  | { | ||
|  |   struct usb_bus *busses; | ||
|  |   struct usb_bus *bus; | ||
|  | 
 | ||
|  |   number_of_hubs_with_feature = 0; | ||
|  |   busses = usb_get_busses(); | ||
|  |   if (busses == NULL) | ||
|  |     { | ||
|  |       perror ("failed to access USB"); | ||
|  |       return -1; | ||
|  |     } | ||
|  | 
 | ||
|  |   for (bus = busses; bus; bus = bus->next) | ||
|  |     { | ||
|  |       struct usb_device *dev; | ||
|  | 
 | ||
|  |       for (dev = bus->devices; dev; dev = dev->next) | ||
|  | 	{ | ||
|  | 	  usb_dev_handle *uh; | ||
|  | 	  int print = 0; | ||
|  | 
 | ||
|  | 	  if (dev->descriptor.bDeviceClass != USB_CLASS_HUB) | ||
|  | 	    continue; | ||
|  | 
 | ||
|  | 	  if (listing | ||
|  | 	      || (verbose | ||
|  | 		  && ((atoi (bus->dirname) == busnum && dev->devnum == devnum) | ||
|  | 		      || hub == number_of_hubs_with_feature))) | ||
|  | 	    print = 1; | ||
|  | 
 | ||
|  | 	  uh = usb_open (dev); | ||
|  | 
 | ||
|  | 	  if (uh != NULL) | ||
|  | 	    { | ||
|  | 	      char buf[1024]; | ||
|  | 	      int len; | ||
|  | 	      int nport; | ||
|  | 	      struct usb_hub_descriptor *uhd = (struct usb_hub_descriptor *)buf; | ||
|  | 	      if ((len = usb_control_msg (uh, USB_DIR_IN | USB_RT_HUB, | ||
|  | 					  USB_REQ_GET_DESCRIPTOR, | ||
|  | 					  USB_DT_HUB << 8, 0,  | ||
|  | 					  buf, sizeof (buf), CTRL_TIMEOUT)) | ||
|  | 		  > sizeof (struct usb_hub_descriptor)) | ||
|  | 		{ | ||
|  | 		  if (!(uhd->wHubCharacteristics[0] & HUB_CHAR_PORTIND) | ||
|  | 		      && (uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM) >= 2) | ||
|  | 		    continue; | ||
|  | 
 | ||
|  | 		  if (print) | ||
|  | 		    printf ("Hub #%d at %s:%03d\n", | ||
|  | 			    number_of_hubs_with_feature, | ||
|  | 			    bus->dirname, dev->devnum); | ||
|  | 
 | ||
|  | 		  switch ((uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM)) | ||
|  | 		    { | ||
|  | 		    case 0: | ||
|  | 		      if (print) | ||
|  | 			fprintf (stderr, " INFO: ganged switching.\n"); | ||
|  | 		      break; | ||
|  | 		    case 1: | ||
|  | 		      if (print) | ||
|  | 			fprintf (stderr, " INFO: individual power switching.\n"); | ||
|  | 		      break; | ||
|  | 		    case 2: | ||
|  | 		    case 3: | ||
|  | 		      if (print) | ||
|  | 			fprintf (stderr, " WARN: No power switching.\n"); | ||
|  | 		      break; | ||
|  | 		    } | ||
|  | 
 | ||
|  | 		  if (print | ||
|  | 		      && !(uhd->wHubCharacteristics[0] & HUB_CHAR_PORTIND)) | ||
|  | 		    fprintf (stderr, " WARN: Port indicators are NOT supported.\n"); | ||
|  | 		} | ||
|  | 	      else | ||
|  | 		{ | ||
|  | 		  perror ("Can't get hub descriptor"); | ||
|  | 		  usb_close (uh); | ||
|  | 		  continue; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	      nport = buf[2]; | ||
|  | 	      hubs[number_of_hubs_with_feature].busnum = atoi (bus->dirname); | ||
|  | 	      hubs[number_of_hubs_with_feature].devnum = dev->devnum; | ||
|  | 	      hubs[number_of_hubs_with_feature].dev = dev; | ||
|  | 	      hubs[number_of_hubs_with_feature].indicator_support = | ||
|  | 		(uhd->wHubCharacteristics[0] & HUB_CHAR_PORTIND)? 1 : 0; | ||
|  | 	      hubs[number_of_hubs_with_feature].nport = nport; | ||
|  | 
 | ||
|  | 	      number_of_hubs_with_feature++; | ||
|  | 
 | ||
|  | 	      if (verbose) | ||
|  | 		hub_port_status (uh, nport); | ||
|  | 
 | ||
|  | 	      usb_close (uh); | ||
|  | 	    } | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |   return number_of_hubs_with_feature; | ||
|  | } | ||
|  | 
 | ||
|  | int | ||
|  | get_hub (int busnum, int devnum) | ||
|  | { | ||
|  |   int i; | ||
|  | 
 | ||
|  |   for (i = 0; i < number_of_hubs_with_feature; i++) | ||
|  |     if (hubs[i].busnum == busnum && hubs[i].devnum == devnum) | ||
|  |       return i; | ||
|  | 
 | ||
|  |   return -1; | ||
|  | } | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * HUB-CTRL  -  program to control port power/led of USB hub | ||
|  |  * | ||
|  |  *   # hub-ctrl                    // List hubs available
 | ||
|  |  *   # hub-ctrl -P 1               // Power off at port 1
 | ||
|  |  *   # hub-ctrl -P 1 -p 1          // Power on at port 1
 | ||
|  |  *   # hub-ctrl -P 2 -l            // LED on at port 1
 | ||
|  |  * | ||
|  |  * Requirement: USB hub which implements port power control / indicator control | ||
|  |  * | ||
|  |  *      Work fine: | ||
|  |  *         Elecom's U2H-G4S: www.elecom.co.jp (indicator depends on power) | ||
|  |  *         04b4:6560 | ||
|  |  * | ||
|  |  *	   Sanwa Supply's USB-HUB14GPH: www.sanwa.co.jp (indicators don't) | ||
|  |  * | ||
|  |  *	   Targus, Inc.'s PAUH212: www.targus.com (indicators don't) | ||
|  |  *         04cc:1521 | ||
|  |  * | ||
|  |  *	   Hawking Technology's UH214: hawkingtech.com (indicators don't) | ||
|  |  * | ||
|  |  */ | ||
|  | 
 | ||
|  | int | ||
|  | main (int argc, const char *argv[]) | ||
|  | { | ||
|  |   int busnum = 0, devnum = 0; | ||
|  |   int cmd = COMMAND_SET_NONE; | ||
|  |   int port = 1; | ||
|  |   int value = 0; | ||
|  |   int request, feature, index; | ||
|  |   int result = 0; | ||
|  |   int listing = 0; | ||
|  |   int verbose = 0; | ||
|  |   int hub = -1; | ||
|  |   usb_dev_handle *uh = NULL; | ||
|  |   int i; | ||
|  | 
 | ||
|  |   if (argc == 1) | ||
|  |     listing = 1; | ||
|  | 
 | ||
|  |   for (i = 1; i < argc; i++) | ||
|  |     if (argv[i][0] == '-') | ||
|  |       switch (argv[i][1]) | ||
|  | 	{ | ||
|  | 	case 'h': | ||
|  | 	  if (++i >= argc || busnum > 0 || devnum > 0) | ||
|  | 	    exit_with_usage (argv[0]); | ||
|  | 	  hub = atoi (argv[i]); | ||
|  | 	  break; | ||
|  | 
 | ||
|  | 	case 'b': | ||
|  | 	  if (++i >= argc || hub >= 0) | ||
|  | 	    exit_with_usage (argv[0]); | ||
|  | 	  busnum = atoi (argv[i]); | ||
|  | 	  break; | ||
|  | 
 | ||
|  | 	case 'd': | ||
|  | 	  if (++i >= argc || hub >= 0) | ||
|  | 	    exit_with_usage (argv[0]); | ||
|  | 	  devnum = atoi (argv[i]); | ||
|  | 	  break; | ||
|  | 
 | ||
|  | 	case 'P': | ||
|  | 	  if (++i >= argc) | ||
|  | 	    exit_with_usage (argv[0]); | ||
|  | 	  port = atoi (argv[i]); | ||
|  | 	  break; | ||
|  | 
 | ||
|  | 	case 'l': | ||
|  | 	  if (cmd != COMMAND_SET_NONE) | ||
|  | 	    exit_with_usage (argv[0]); | ||
|  | 	  if (++i < argc) | ||
|  | 	    value = atoi (argv[i]); | ||
|  | 	  else | ||
|  | 	    value = HUB_LED_GREEN; | ||
|  | 	  cmd = COMMAND_SET_LED; | ||
|  | 	  break; | ||
|  | 
 | ||
|  | 	case 'p': | ||
|  | 	  if (cmd != COMMAND_SET_NONE) | ||
|  | 	    exit_with_usage (argv[0]); | ||
|  | 	  if (++i < argc) | ||
|  | 	    value = atoi (argv[i]); | ||
|  | 	  else | ||
|  | 	    value= 0; | ||
|  | 	  cmd = COMMAND_SET_POWER; | ||
|  | 	  break; | ||
|  | 
 | ||
|  | 	case 'v': | ||
|  | 	  verbose = 1; | ||
|  | 	  if (argc == 2) | ||
|  | 	    listing = 1; | ||
|  | 	  break; | ||
|  | 
 | ||
|  | 	default: | ||
|  | 	  exit_with_usage (argv[0]); | ||
|  | 	} | ||
|  |     else | ||
|  |       exit_with_usage (argv[0]); | ||
|  | 
 | ||
|  |   if ((busnum > 0 && devnum <= 0) || (busnum <= 0 && devnum > 0)) | ||
|  |     /* BUS is specified, but DEV is'nt, or ... */ | ||
|  |     exit_with_usage (argv[0]); | ||
|  | 
 | ||
|  |   /* Default is the hub #0 */ | ||
|  |   if (hub < 0 && busnum == 0) | ||
|  |     hub = 0; | ||
|  | 
 | ||
|  |   /* Default is POWER */ | ||
|  |   if (cmd == COMMAND_SET_NONE) | ||
|  |     cmd = COMMAND_SET_POWER; | ||
|  | 
 | ||
|  |   usb_init (); | ||
|  |   usb_find_busses (); | ||
|  |   usb_find_devices (); | ||
|  | 
 | ||
|  |   if (usb_find_hubs (listing, verbose, busnum, devnum, hub) <= 0) | ||
|  |     { | ||
|  |       fprintf (stderr, "No hubs found.\n"); | ||
|  |       exit (1); | ||
|  |     } | ||
|  | 
 | ||
|  |   if (listing) | ||
|  |     exit (0); | ||
|  | 
 | ||
|  |   if (hub < 0) | ||
|  |     hub = get_hub (busnum, devnum); | ||
|  | 
 | ||
|  |   if (hub >= 0 && hub < number_of_hubs_with_feature) | ||
|  |     uh = usb_open (hubs[hub].dev); | ||
|  | 
 | ||
|  |   if (uh == NULL) | ||
|  |     { | ||
|  |       fprintf (stderr, "Device not found.\n"); | ||
|  |       result = 1; | ||
|  |     } | ||
|  |   else | ||
|  |     { | ||
|  |       if (cmd == COMMAND_SET_POWER) | ||
|  | 	if (value) | ||
|  | 	  { | ||
|  | 	    request = USB_REQ_SET_FEATURE; | ||
|  | 	    feature = USB_PORT_FEAT_POWER; | ||
|  | 	    index = port; | ||
|  | 	  } | ||
|  | 	else | ||
|  | 	  { | ||
|  | 	    request = USB_REQ_CLEAR_FEATURE; | ||
|  | 	    feature = USB_PORT_FEAT_POWER; | ||
|  | 	    index = port; | ||
|  | 	  } | ||
|  |       else | ||
|  | 	{ | ||
|  | 	  request = USB_REQ_SET_FEATURE; | ||
|  | 	  feature = USB_PORT_FEAT_INDICATOR; | ||
|  | 	  index = (value << 8) | port; | ||
|  | 	} | ||
|  | 
 | ||
|  |       if (verbose) | ||
|  | 	printf ("Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d)\n", | ||
|  | 		request, feature, index); | ||
|  | 
 | ||
|  |       if (usb_control_msg (uh, USB_RT_PORT, request, feature, index, | ||
|  | 			   NULL, 0, CTRL_TIMEOUT) < 0) | ||
|  | 	{ | ||
|  | 	  perror ("failed to control.\n"); | ||
|  | 	  result = 1; | ||
|  | 	} | ||
|  | 
 | ||
|  |       if (verbose) | ||
|  | 	hub_port_status (uh, hubs[hub].nport); | ||
|  | 
 | ||
|  |       usb_close (uh); | ||
|  |     } | ||
|  | 
 | ||
|  |   exit (result); | ||
|  | } |