#! /usr/bin/python
# -*- coding: utf-8 -*- 
#
# Generic utility for sending hex commands to usb device
# (C) Alexey Lubimov 
# http://www.atmsk.ru
# 
# usbtool are GPL
# 
#

import sys
import usb
import struct 
from  optparse import OptionParser

#constants

known_devices={ 
    (0x046d,0xc294): "G25 (normal mode)",
    (0x046d,0xc299): "G25 (extended mode)",
    (0x046d,0xc298): "G25 (force pro mode)"
}

known_commands = {
# set mode
    "g25-set-extended-mode":"f810",
    "g25-set-pro-mode":"f801",

    "g25-set-right-side-max-push":  "210b8080 f000ff",
    "g25-set-right-side-max-suck":  "210b8080 f010ff",
    "g25-set-right-side-half-push": "210b8080 8000ff",
    "g25-set-right-side-half-suck": "210b8080 8010ff",

    "g25-set-left-side-max-push":  "210b8080 0f00ff",
    "g25-set-left-side-max-suck":  "210b8080 0f01ff",
    "g25-set-left-side-half-push": "210b8080 0700ff",
    "g25-set-left-side-half-suck": "210b8080 0801ff",

# unlock the full range of the wheel     
    "g25-set-range-wheel-40":  "f881 28 00 000000", 
    "g25-set-range-wheel-900": "f881 84 03 000000" 
    
}


class cl_opts(OptionParser):
    def __init__(self):
    	VERSION="%prog v:0.1"
	usage = """usage: %prog [options] [command] [command] ...
	examples: 
	usbtool --list
	usbtool --list-commands
	usbtool g25-set-extended-mode
	usbtool f810
	"""
	OptionParser.__init__(self,version=VERSION,usage=usage)
        self.add_option("-v", action="store_true", dest="verbose", help="verbose output",default=False)
        self.add_option("-d", dest="device", help="specify device as in 'usbtool --list'", metavar="xxxx:yyyy:z")
        self.add_option("-l","--list", action="callback", callback=list, help="print list usb devices", nargs=0)
        self.add_option("--list-commands", action="callback", callback=list_commands, help="print known commands", nargs=0)
                                                                            


def scan_devices():
    #scan for all suitable usb devices
    usb.UpdateLists()
    devices={}
    for bus in usb.AllBusses():
	for device in bus.devices():
    	    klass,subclass,proto=device.classdetails()
    	    if klass != usb.USB_CLASS_HUB:
    		for iface in device.interfaces():
		    devices[(device.vendor(), device.product(),iface.number())]=device
    return devices
    
def list(arg1=None,arg2=None,arg3=None,arg4=None):
    devices=scan_devices()
    print "List devices:"
    for dev in devices.keys():
	line = "%04x:%04x:%01d " % (dev)
	# detect known devices
	if known_devices.has_key((dev[0],dev[1])):
	    line+= "Device %s found!" % known_devices[(dev[0],dev[1])]
	else:
    	    try:
    		line+="%s/%s" % (devices[dev].vendorstring(), devices[dev].productstring())
    	    except usb.USBException,e: 
    		line+=" %s" % e
	print line
    sys.exit(0)



def list_commands(arg1=None,arg2=None,arg3=None,arg4=None):

    print "Commands:"
    for i in known_commands.keys():
	print "usbtool %-30s  or usbtool %-20s " % (i,known_commands[i])
    sys.exit(0)


def select_device(devices):
    device=() 
    if options.device!=None:
	dev=options.device.split(":")
	try:
	    device=(int(dev[0],16),int(dev[1],16),int(dev[2],16))
	except:
	    sys.exit("Bad format device %s!\nexpected, for example, 046d:c299:0 (see  --list output)" % options.device)
    else:
	for dev in devices.keys():
	    if known_devices.has_key((dev[0],dev[1])):
		if options.verbose:
		    print "autoselect first known device %s at %x:%x:%x " % (known_devices[(dev[0],dev[1])],dev[0],dev[1],dev[2])
		device=(dev[0],dev[1],dev[2])
		break

    if not devices.has_key(device):
	sys.exit("device %s not found!" % options.device)
    return device


if len(sys.argv)==1:
                sys.argv.append('-h')
parser=cl_opts()
(options, args) = parser.parse_args()

device=select_device(scan_devices())


buf=""
for command in args:
    if known_commands.has_key(command):
	if options.verbose:
	    print "Command alias detected: %s" % command
	command=known_commands[command]
    if options.verbose:
	print "Send command %s" % command
    command=command.replace(" ","")
    i=0
    end=len(command)
    if (end % 2) > 0:
	sys.exit("Command not packed to bytes. Should be xxyyzz")
    while i < end:
	try:
	    buf+=struct.pack("B",int(command[i]+command[i+1],16))
	except ValueError,e:
	    sys.exit("conversion from hex string to int failed, %s " % e)
	i+=2



if len(buf)!=0:
    if options.verbose:
	print "opening device"
    try:
	dev=usb.OpenDevice(device[0],device[1], device[2])
	dev.write(buf)
        dev.close()
    except usb.USBException,e: 
	print "error: %s" % e
else:
    if options.verbose:
	print "nothing send!"                    
