123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- """
- # Razer device configuration
- # High level user interface library
- #
- # This library connects to the lowlevel 'razerd' system daemon.
- #
- # Copyright (C) 2008-2016 Michael Buesch <m@bues.ch>
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # of the License, or (at your option) any later version.
- #
- # This program 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 General Public License for more details.
- """
- import sys
- if sys.version_info[0] != 3:
- print("Python %d is not supported by razercfg." % sys.version_info[0])
- print("Please install Python 3.x")
- sys.exit(1)
- import socket
- import select
- import hashlib
- import struct
- RAZER_VERSION = "0.36"
- class RazerEx(Exception):
- "Exception thrown by pyrazer code."
- __be32_struct = struct.Struct(">I")
- __be16_struct = struct.Struct(">H")
- def razer_be32_to_int(be32, offset=0):
- return __be32_struct.unpack_from(be32, offset)[0]
- def razer_be16_to_int(be16, offset=0):
- return __be16_struct.unpack_from(be16, offset)[0]
- def razer_int_to_be32(integer):
- return __be32_struct.pack(integer)
- def razer_int_to_be16(integer):
- return __be16_struct.pack(integer)
- def razer_str2bool(string):
- string = string.lower().strip()
- if string in ["no", "off", "false"]:
- return False
- if string in ["yes", "on", "true"]:
- return True
- try:
- return bool(int(string))
- except ValueError as e:
- pass
- raise ValueError
- class RazerDevId(object):
- "devid parser"
- DEVTYPE_UNKNOWN = "Unknown"
- DEVTYPE_MOUSE = "Mouse"
- BUSTYPE_UNKNOWN = "Unknown"
- BUSTYPE_USB = "USB"
- def __init__(self, devid):
- self.devtype = self.DEVTYPE_UNKNOWN
- self.bustype = self.BUSTYPE_UNKNOWN
- self.buspos = ""
- self.devname = ""
- self.devid = ""
- try:
- id = devid.split(':')
- self.devtype = id[0]
- self.devname = id[1]
- bus = id[2].split('-')
- self.bustype = bus[0]
- self.buspos = bus[1]
- if len(bus) >= 3:
- self.buspos += "-" + bus[2]
- self.devid = id[3]
- except IndexError:
- pass
- def getDevType(self):
- "Returns DEVTYPE_..."
- return self.devtype
- def getBusType(self):
- "Returns BUSTYPE_..."
- return self.bustype
- def getBusPosition(self):
- "Returns the bus position ID string"
- return self.buspos
- def getDevName(self):
- "Returns the device name string"
- return self.devname
- def getDevId(self):
- "Returns the device ID string"
- return self.devid
- class RazerRGB(object):
- "An RGB color"
- def __init__(self, r, g, b):
- self.r = r
- self.g = g
- self.b = b
- @classmethod
- def fromU32(cls, u32):
- return cls(r=(u32 >> 16) & 0xFF,
- g=(u32 >> 8) & 0xFF,
- b=(u32 >> 0) & 0xFF)
- def toU32(self):
- return ((self.r & 0xFF) << 16) |\
- ((self.g & 0xFF) << 8) |\
- ((self.b & 0xFF) << 0)
- @classmethod
- def fromString(cls, string):
- string = string.strip().lstrip("#")
- if len(string) != 6:
- raise ValueError
- return cls(r=int(string[0:2], 16),
- g=int(string[2:4], 16),
- b=int(string[4:6], 16))
- class RazerLEDMode(object):
- "Representation of LED mode"
- LED_MODE_STATIC = 0
- LED_MODE_SPECTRUM = 1
- LED_MODE_BREATHING = 2
- LED_MODE_WAVE = 3
- LED_MODE_REACTION = 4
- def __init__(self, val):
- self.val = val
- def toString(self):
- return {
- self.LED_MODE_STATIC: 'static',
- self.LED_MODE_SPECTRUM: 'spectrum',
- self.LED_MODE_BREATHING: 'breathing',
- self.LED_MODE_WAVE: 'wave',
- self.LED_MODE_REACTION: 'reaction'
- }[self.val]
- @classmethod
- def listFromSupportedModes(cls, mask):
- modes = []
- for mode in (cls.LED_MODE_STATIC, cls.LED_MODE_SPECTRUM, cls.LED_MODE_BREATHING, cls.LED_MODE_WAVE, cls.LED_MODE_REACTION):
- if mask & (1 << mode):
- modes.append(cls(mode))
- return modes
- @classmethod
- def fromString(cls, string):
- return {
- 'static': cls(cls.LED_MODE_STATIC),
- 'spectrum': cls(cls.LED_MODE_SPECTRUM),
- 'breathing': cls(cls.LED_MODE_BREATHING),
- 'wave': cls(cls.LED_MODE_WAVE),
- 'reaction': cls(cls.LED_MODE_REACTION)
-
- }[string]
- class RazerLED(object):
- "LED representation"
- def __init__(self, profileId, name, state, mode, supported_modes, color, canChangeColor):
- self.profileId = profileId
- self.name = name
- self.state = state
- self.mode = mode
- self.supported_modes = supported_modes
- self.color = color
- self.canChangeColor = canChangeColor
- class RazerDpiMapping(object):
- "DPI mapping"
- def __init__(self, id, res, profileMask, mutable):
- self.id = id
- self.res = res
- self.profileMask = profileMask
- self.mutable = mutable
- class Razer(object):
- SOCKET_PATH = "/var/run/razerd/socket"
- PRIVSOCKET_PATH = "/var/run/razerd/socket.privileged"
- INTERFACE_REVISION = 6
- COMMAND_MAX_SIZE = 512
- COMMAND_HDR_SIZE = 1
- BULK_CHUNK_SIZE = 128
- RAZER_IDSTR_MAX_SIZE = 128
- RAZER_LEDNAME_MAX_SIZE = 64
- RAZER_NR_DIMS = 3
- COMMAND_ID_GETREV = 0 # Get the revision number of the socket interface.
- COMMAND_ID_RESCANMICE = 1 # Rescan mice.
- COMMAND_ID_GETMICE = 2 # Get a list of detected mice.
- COMMAND_ID_GETFWVER = 3 # Get the firmware rev of a mouse.
- COMMAND_ID_SUPPFREQS = 4 # Get a list of supported frequencies.
- COMMAND_ID_SUPPRESOL = 5 # Get a list of supported resolutions.
- COMMAND_ID_SUPPDPIMAPPINGS = 6 # Get a list of supported DPI mappings.
- COMMAND_ID_CHANGEDPIMAPPING = 7 # Modify a DPI mapping.
- COMMAND_ID_GETDPIMAPPING = 8 # Get the active DPI mapping for a profile.
- COMMAND_ID_SETDPIMAPPING = 9 # Set the active DPI mapping for a profile.
- COMMAND_ID_GETLEDS = 10 # Get a list of LEDs on the device.
- COMMAND_ID_SETLED = 11 # Set the state of a LED.
- COMMAND_ID_GETFREQ = 12 # Get the current frequency.
- COMMAND_ID_SETFREQ = 13 # Set the frequency.
- COMMAND_ID_GETPROFILES = 14 # Get a list of supported profiles.
- COMMAND_ID_GETACTIVEPROF = 15 # Get the active profile.
- COMMAND_ID_SETACTIVEPROF = 16 # Set the active profile.
- COMMAND_ID_SUPPBUTTONS = 17 # Get a list of physical buttons.
- COMMAND_ID_SUPPBUTFUNCS = 18 # Get a list of supported button functions.
- COMMAND_ID_GETBUTFUNC = 19 # Get the current function of a button.
- COMMAND_ID_SETBUTFUNC = 20 # Set the current function of a button.
- COMMAND_ID_SUPPAXES = 21 # Get a list of supported axes.
- COMMAND_ID_RECONFIGMICE = 22 # Reconfigure all mice
- COMMAND_ID_GETMOUSEINFO = 23 # Get detailed information about a mouse
- COMMAND_ID_GETPROFNAME = 24 # Get a profile name.
- COMMAND_ID_SETPROFNAME = 25 # Set a profile name.
- COMMAND_PRIV_FLASHFW = 128 # Upload and flash a firmware image
- COMMAND_PRIV_CLAIM = 129 # Claim the device.
- COMMAND_PRIV_RELEASE = 130 # Release the device.
- # Replies to commands
- REPLY_ID_U32 = 0 # An unsigned 32bit integer.
- REPLY_ID_STR = 1 # A string
- # Notifications. These go through the reply channel.
- __NOTIFY_ID_FIRST = 128
- NOTIFY_ID_NEWMOUSE = 128 # New mouse was connected.
- NOTIFY_ID_DELMOUSE = 129 # A mouse was removed.
- # String encodings
- STRING_ENC_ASCII = 0
- STRING_ENC_UTF8 = 1
- STRING_ENC_UTF16BE = 2
- ERR_NONE = 0
- ERR_CMDSIZE = 1
- ERR_NOMEM = 2
- ERR_NOMOUSE = 3
- ERR_NOLED = 4
- ERR_CLAIM = 5
- ERR_FAIL = 6
- ERR_PAYLOAD = 7
- ERR_NOTSUPP = 8
- errorToStringMap = {
- ERR_NONE : "Success",
- ERR_CMDSIZE : "Invalid command size",
- ERR_NOMEM : "Out of memory",
- ERR_NOMOUSE : "Could not find mouse",
- ERR_NOLED : "Could not find LED",
- ERR_CLAIM : "Failed to claim device",
- ERR_FAIL : "Failure",
- ERR_PAYLOAD : "Payload error",
- ERR_NOTSUPP : "Operation not supported",
- }
- # Axis flags
- RAZER_AXIS_INDEPENDENT_DPIMAPPING = (1 << 0)
- # Mouseinfo flags
- MOUSEINFOFLG_RESULTOK = (1 << 0) # Other flags are ok, if this is set.
- MOUSEINFOFLG_GLOBAL_LEDS = (1 << 1) # The device has global LEDs.
- MOUSEINFOFLG_PROFILE_LEDS = (1 << 2) # The device has per-profile LEDs.
- MOUSEINFOFLG_GLOBAL_FREQ = (1 << 3) # The device has global frequency settings.
- MOUSEINFOFLG_PROFILE_FREQ = (1 << 4) # The device has per-profile frequency settings.
- MOUSEINFOFLG_PROFNAMEMUTABLE = (1 << 5) # Profile names can be changed.
- MOUSEINFOFLG_SUGGESTFWUP = (1 << 6) # A firmware update is suggested.
- # LED flags
- LED_FLAG_HAVECOLOR = (1 << 0)
- LED_FLAG_CHANGECOLOR = (1 << 1)
- # Special profile ID
- PROFILE_INVALID = 0xFFFFFFFF
- @staticmethod
- def strerror(errno):
- try:
- errstr = Razer.errorToStringMap[errno]
- except KeyError:
- errstr = "Unknown error"
- return "Errorcode %d: %s" % (errno, errstr)
- def __init__(self, enableNotifications=False):
- "Connect to razerd."
- self.enableNotifications = enableNotifications
- self.notifications = []
- try:
- self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- self.sock.connect(self.SOCKET_PATH)
- except socket.error as e:
- raise RazerEx("Failed to connect to razerd socket: %s" % e)
- try:
- self.privsock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- self.privsock.connect(self.PRIVSOCKET_PATH)
- except socket.error as e:
- self.privsock = None # No privileged access
- self.__sendCommand(self.COMMAND_ID_GETREV)
- rev = self.__recvU32()
- if (rev != self.INTERFACE_REVISION):
- additional = ""
- if rev < self.INTERFACE_REVISION:
- additional = "\nThe running razerd is too old. " \
- "Try to delete all razerd binaries and " \
- "re-install the razercfg package."
- raise RazerEx("Incompatible razerd daemon socket interface revision.\n"
- "razerd reported revision %u, but we expected revision %u."
- "%s" %\
- (rev, self.INTERFACE_REVISION, additional))
- def __constructCommand(self, commandId, idstr, payload):
- cmd = bytes((commandId,))
- idstr = idstr.encode("UTF-8")
- idstr += b'\0' * (self.RAZER_IDSTR_MAX_SIZE - len(idstr))
- cmd += idstr
- cmd += payload
- cmd += b'\0' * (self.COMMAND_MAX_SIZE - len(cmd))
- return cmd
- def __send(self, data):
- self.sock.sendall(data)
- def __sendPrivileged(self, data):
- try:
- self.privsock.sendall(data)
- except (socket.error, AttributeError) as e:
- raise RazerEx("Privileged command failed. Do you have permission?")
- def __sendBulkPrivileged(self, data):
- for i in range(0, len(data), self.BULK_CHUNK_SIZE):
- chunk = data[i : i + self.BULK_CHUNK_SIZE]
- self.__sendPrivileged(chunk)
- result = self.__recvU32Privileged()
- if result != 0:
- raise RazerEx("Privileged bulk write failed. %u" % result)
- def __sendCommand(self, commandId, idstr="", payload=b""):
- cmd = self.__constructCommand(commandId, idstr, payload)
- self.__send(cmd)
- def __sendPrivilegedCommand(self, commandId, idstr="", payload=b""):
- cmd = self.__constructCommand(commandId, idstr, payload)
- self.__sendPrivileged(cmd)
- def __handleReceivedMessage(self, packet):
- id = packet[0]
- if id < self.__NOTIFY_ID_FIRST:
- raise RazerEx("Received unhandled packet %u" % id)
- if self.enableNotifications:
- self.notifications.append(packet)
- def __receive(self, sock):
- "Receive the next message. This will block until a message arrives."
- hdrlen = 1
- hdr = sock.recv(hdrlen)
- id = hdr[0]
- payload = None
- if id == self.REPLY_ID_U32:
- payload = razer_be32_to_int(sock.recv(4))
- elif id == self.REPLY_ID_STR:
- encoding = sock.recv(1)[0]
- strlen = razer_be16_to_int(sock.recv(2))
- if encoding == self.STRING_ENC_ASCII:
- nrbytes = strlen
- decode = lambda pl: pl.decode("ASCII")
- elif encoding == self.STRING_ENC_UTF8:
- nrbytes = strlen
- decode = lambda pl: pl.decode("UTF-8")
- elif encoding == self.STRING_ENC_UTF16BE:
- nrbytes = strlen * 2
- decode = lambda pl: pl.decode("UTF-16-BE")
- else:
- raise RazerEx("Received invalid string encoding %d" %\
- encoding)
- payload = sock.recv(nrbytes) if nrbytes else b""
- try:
- payload = decode(payload)
- except UnicodeError as e:
- raise RazerEx("Unicode decode error in received payload")
- elif id == self.NOTIFY_ID_NEWMOUSE:
- pass
- elif id == self.NOTIFY_ID_DELMOUSE:
- pass
- else:
- raise RazerEx("Received unknown message (id=%u)" % id)
- return (id, payload)
- def __receiveExpectedMessage(self, sock, expectedId):
- """Receive messages until the expected one appears.
- Unexpected messages will be handled by __handleReceivedMessage.
- This function returns the payload of the expected message."""
- while 1:
- id, payload = self.__receive(sock)
- if id == expectedId:
- break
- else:
- self.__handleReceivedMessage((id, payload))
- return payload
- def __recvU32(self):
- "Receive an expected REPLY_ID_U32"
- return self.__receiveExpectedMessage(self.sock, self.REPLY_ID_U32)
- def __recvU32Privileged(self):
- "Receive an expected REPLY_ID_U32 on the privileged socket"
- try:
- return self.__receiveExpectedMessage(self.privsock, self.REPLY_ID_U32)
- except (socket.error, AttributeError) as e:
- raise RazerEx("Privileged recvU32 failed. Do you have permission?")
- def __recvString(self):
- "Receive an expected REPLY_ID_STR"
- return self.__receiveExpectedMessage(self.sock, self.REPLY_ID_STR)
- def pollNotifications(self):
- "Returns a list of pending notifications (id, payload)"
- if not self.enableNotifications:
- raise RazerEx("Polled notifications while notifications were disabled")
- while 1:
- res = select.select([self.sock], [], [], 0.001)
- if not res[0]:
- break
- pack = self.__receive(self.sock)
- self.__handleReceivedMessage(pack)
- notifications = self.notifications
- self.notifications = []
- return notifications
- def rescanMice(self):
- "Send the command to rescan for mice to the daemon."
- self.__sendCommand(self.COMMAND_ID_RESCANMICE)
- def rescanDevices(self):
- "Rescan for new devices."
- self.rescanMice()
- def getMice(self):
- "Returns a list of ID-strings for the detected mice."
- self.__sendCommand(self.COMMAND_ID_GETMICE)
- count = self.__recvU32()
- mice = []
- for i in range(0, count):
- mice.append(self.__recvString())
- return mice
- def getMouseInfo(self, idstr):
- "Get detailed information about a mouse"
- self.__sendCommand(self.COMMAND_ID_GETMOUSEINFO, idstr)
- flags = self.__recvU32()
- if (flags & self.MOUSEINFOFLG_RESULTOK) == 0:
- raise RazerEx("Failed to get mouseinfo for " + idstr)
- return flags
- def reconfigureMice(self):
- "Reconfigure all mice."
- self.__sendCommand(self.COMMAND_ID_RECONFIGMICE)
- def reconfigureDevices(self):
- "Reconfigure all devices."
- self.reconfigureMice()
- def getFwVer(self, idstr):
- "Returns the firmware version. The returned value is a tuple (major, minor)."
- self.__sendCommand(self.COMMAND_ID_GETFWVER, idstr)
- rawVer = self.__recvU32()
- return ((rawVer >> 8) & 0xFF, rawVer & 0xFF)
- def getSupportedFreqs(self, idstr):
- "Returns a list of supported frequencies for a mouse."
- self.__sendCommand(self.COMMAND_ID_SUPPFREQS, idstr)
- count = self.__recvU32()
- freqs = []
- for i in range(0, count):
- freqs.append(self.__recvU32())
- return freqs
- def getCurrentFreq(self, idstr, profileId=PROFILE_INVALID):
- "Returns the currently selected frequency for a mouse."
- payload = razer_int_to_be32(profileId)
- self.__sendCommand(self.COMMAND_ID_GETFREQ, idstr, payload)
- return self.__recvU32()
- def getSupportedRes(self, idstr):
- "Returns a list of supported resolutions for a mouse."
- self.__sendCommand(self.COMMAND_ID_SUPPRESOL, idstr)
- count = self.__recvU32()
- res = []
- for i in range(0, count):
- res.append(self.__recvU32())
- return res
- def getLeds(self, idstr, profileId=PROFILE_INVALID):
- """Returns a list of RazerLED instances for the given profile,
- or the global LEDs, if no profile given"""
- payload = razer_int_to_be32(profileId)
- self.__sendCommand(self.COMMAND_ID_GETLEDS, idstr, payload)
- count = self.__recvU32()
- leds = []
- for i in range(0, count):
- flags = self.__recvU32()
- name = self.__recvString()
- state = self.__recvU32()
- mode = RazerLEDMode(self.__recvU32())
- supported_modes = RazerLEDMode.listFromSupportedModes(self.__recvU32())
- color = self.__recvU32()
- if (flags & self.LED_FLAG_HAVECOLOR) == 0:
- color = None
- else:
- color = RazerRGB.fromU32(color)
- canChangeColor = bool(flags & self.LED_FLAG_CHANGECOLOR)
- leds.append(RazerLED(profileId, name, state, mode, supported_modes, color, canChangeColor))
- return leds
- def setLed(self, idstr, led):
- "Set a LED to a new state."
- if len(led.name) > self.RAZER_LEDNAME_MAX_SIZE:
- raise RazerEx("LED name string too long")
- payload = razer_int_to_be32(led.profileId)
- led_name = led.name.encode("UTF-8")
- payload += led_name
- payload += b'\0' * (self.RAZER_LEDNAME_MAX_SIZE - len(led_name))
- payload += b'\x01' if led.state else b'\x00'
- payload += bytes([led.mode.val])
- if led.color:
- payload += razer_int_to_be32(led.color.toU32())
- else:
- payload += razer_int_to_be32(0)
- self.__sendCommand(self.COMMAND_ID_SETLED, idstr, payload)
- return self.__recvU32()
- def setFrequency(self, idstr, profileId, newFrequency):
- "Set a new scan frequency (in Hz)."
- payload = razer_int_to_be32(profileId) + razer_int_to_be32(newFrequency)
- self.__sendCommand(self.COMMAND_ID_SETFREQ, idstr, payload)
- return self.__recvU32()
- def getSupportedDpiMappings(self, idstr):
- "Returns a list of supported DPI mappings. Each entry is a RazerDpiMapping() instance."
- self.__sendCommand(self.COMMAND_ID_SUPPDPIMAPPINGS, idstr)
- count = self.__recvU32()
- mappings = []
- for i in range(0, count):
- id = self.__recvU32()
- dimMask = self.__recvU32()
- res = []
- for i in range(0, self.RAZER_NR_DIMS):
- rVal = self.__recvU32()
- if (dimMask & (1 << i)) == 0:
- rVal = None
- res.append(rVal)
- profileMaskHigh = self.__recvU32()
- profileMaskLow = self.__recvU32()
- profileMask = (profileMaskHigh << 32) | profileMaskLow
- mutable = self.__recvU32()
- mappings.append(RazerDpiMapping(
- id, res, profileMask, mutable))
- return mappings
- def changeDpiMapping(self, idstr, mappingId, dimensionId, newResolution):
- "Changes the resolution value of a DPI mapping."
- payload = razer_int_to_be32(mappingId) +\
- razer_int_to_be32(dimensionId) +\
- razer_int_to_be32(newResolution)
- self.__sendCommand(self.COMMAND_ID_CHANGEDPIMAPPING, idstr, payload)
- return self.__recvU32()
- def getDpiMapping(self, idstr, profileId, axisId=None):
- "Gets the resolution mapping of a profile."
- if axisId is None:
- axisId = 0xFFFFFFFF
- payload = razer_int_to_be32(profileId) +\
- razer_int_to_be32(axisId)
- self.__sendCommand(self.COMMAND_ID_GETDPIMAPPING, idstr, payload)
- return self.__recvU32()
- def setDpiMapping(self, idstr, profileId, mappingId, axisId=None):
- "Sets the resolution mapping of a profile."
- if axisId is None:
- axisId = 0xFFFFFFFF
- payload = razer_int_to_be32(profileId) +\
- razer_int_to_be32(axisId) +\
- razer_int_to_be32(mappingId)
- self.__sendCommand(self.COMMAND_ID_SETDPIMAPPING, idstr, payload)
- return self.__recvU32()
- def getProfiles(self, idstr):
- "Returns a list of profiles. Each entry is the profile ID."
- self.__sendCommand(self.COMMAND_ID_GETPROFILES, idstr)
- count = self.__recvU32()
- profiles = []
- for i in range(0, count):
- profiles.append(self.__recvU32())
- return profiles
- def getActiveProfile(self, idstr):
- "Returns the ID of the active profile."
- self.__sendCommand(self.COMMAND_ID_GETACTIVEPROF, idstr)
- return self.__recvU32()
- def setActiveProfile(self, idstr, profileId):
- "Selects the active profile."
- payload = razer_int_to_be32(profileId)
- self.__sendCommand(self.COMMAND_ID_SETACTIVEPROF, idstr, payload)
- return self.__recvU32()
- def getProfileName(self, idstr, profileId):
- "Get a profile name."
- payload = razer_int_to_be32(profileId)
- self.__sendCommand(self.COMMAND_ID_GETPROFNAME, idstr, payload)
- return self.__recvString()
- def setProfileName(self, idstr, profileId, newName):
- "Set a profile name. newName is expected to be unicode."
- payload = razer_int_to_be32(profileId)
- rawstr = newName.encode("UTF-16-BE")
- rawstr = rawstr[:min(len(rawstr), 64 * 2)]
- rawstr += b'\0' * (64 * 2 - len(rawstr))
- payload += rawstr
- self.__sendCommand(self.COMMAND_ID_SETPROFNAME, idstr, payload)
- return self.__recvU32()
- def flashFirmware(self, idstr, image):
- "Flash a new firmware on the device. Needs high privileges!"
- payload = razer_int_to_be32(len(image))
- self.__sendPrivilegedCommand(self.COMMAND_PRIV_FLASHFW, idstr, payload)
- self.__sendBulkPrivileged(image)
- return self.__recvU32Privileged()
- def getSupportedButtons(self, idstr):
- "Get a list of supported buttons. Each entry is a tuple (id, name)."
- self.__sendCommand(self.COMMAND_ID_SUPPBUTTONS, idstr)
- buttons = []
- count = self.__recvU32()
- for i in range(0, count):
- id = self.__recvU32()
- name = self.__recvString()
- buttons.append( (id, name) )
- return buttons
- def getSupportedButtonFunctions(self, idstr):
- "Get a list of possible button functions. Each entry is a tuple (id, name)."
- self.__sendCommand(self.COMMAND_ID_SUPPBUTFUNCS, idstr)
- funcs = []
- count = self.__recvU32()
- for i in range(0, count):
- id = self.__recvU32()
- name = self.__recvString()
- funcs.append( (id, name) )
- return funcs
- def getButtonFunction(self, idstr, profileId, buttonId):
- "Get a button function. Returns a tuple (id, name)."
- payload = razer_int_to_be32(profileId) + razer_int_to_be32(buttonId)
- self.__sendCommand(self.COMMAND_ID_GETBUTFUNC, idstr, payload)
- id = self.__recvU32()
- name = self.__recvString()
- return (id, name)
- def setButtonFunction(self, idstr, profileId, buttonId, functionId):
- "Set a button function."
- payload = razer_int_to_be32(profileId) +\
- razer_int_to_be32(buttonId) +\
- razer_int_to_be32(functionId)
- self.__sendCommand(self.COMMAND_ID_SETBUTFUNC, idstr, payload)
- return self.__recvU32()
- def getSupportedAxes(self, idstr):
- "Get a list of axes on the device. Each entry is a tuple (id, name, flags)."
- self.__sendCommand(self.COMMAND_ID_SUPPAXES, idstr)
- axes = []
- count = self.__recvU32()
- for i in range(0, count):
- id = self.__recvU32()
- name = self.__recvString()
- flags = self.__recvU32()
- axes.append( (id, name, flags) )
- return axes
- class IHEXParser(object):
- TYPE_DATA = 0
- TYPE_EOF = 1
- TYPE_ESAR = 2
- TYPE_SSAR = 3
- TYPE_ELAR = 4
- TYPE_SLAR = 5
- def __init__(self, ihex):
- self.ihex = ihex
- def parse(self):
- bin = []
- try:
- lines = self.ihex.decode("ASCII").splitlines()
- hiAddr = 0
- for line in lines:
- line = line.strip()
- if len(line) == 0:
- continue
- if len(line) < 11 or (len(line) - 1) % 2 != 0:
- raise RazerEx("Invalid firmware file format (IHEX length error)")
- if line[0] != ':':
- raise RazerEx("Invalid firmware file format (IHEX magic error)")
- count = int(line[1:3], 16)
- if len(line) != count * 2 + 11:
- raise RazerEx("Invalid firmware file format (IHEX count error)")
- addr = (int(line[3:5], 16) << 8) | int(line[5:7], 16)
- addr |= hiAddr << 16
- type = int(line[7:9], 16)
- checksum = 0
- for i in range(1, len(line), 2):
- byte = int(line[i:i+2], 16)
- checksum = (checksum + byte) & 0xFF
- checksum = checksum & 0xFF
- if checksum != 0:
- raise RazerEx("Invalid firmware file format (IHEX checksum error)")
- if type == self.TYPE_EOF:
- break
- if type == self.TYPE_ELAR:
- if count != 2:
- raise RazerEx("Invalid firmware file format (IHEX inval ELAR)")
- hiAddr = (int(line[9:11], 16) << 8) | int(line[11:13], 16)
- continue
- if type == self.TYPE_DATA:
- if len(bin) < addr + count: # Reallocate
- bin += [b'\0'] * (addr + count - len(bin))
- for i in range(9, 9 + count * 2, 2):
- byte = bytes( (int(line[i:i+2], 16), ) )
- if bin[(i - 9) // 2 + addr] != b'\0':
- raise RazerEx("Invalid firmware file format (IHEX corruption)")
- bin[(i - 9) // 2 + addr] = byte
- continue
- raise RazerEx("Invalid firmware file format (IHEX unsup type %d)" % type)
- except (ValueError, UnicodeError) as e:
- raise RazerEx("Invalid firmware file format (IHEX digit format)")
- return b"".join(bin)
- class RazerFirmwareParser(object):
- class Descriptor:
- def __init__(self, startOffset, endOffset, parser, binTruncate):
- # startOffset: The offset where the ihex/srec/etc starts
- # endOffset: The offset where the ihex/srec/etc ends
- # parser: ihex/srec/etc parser
- # binTruncate: Number of bytes to truncate the binary to
- self.start = startOffset
- self.len = endOffset - startOffset + 1
- self.parser = parser
- self.binTruncate = binTruncate
- DUMP = 0 # Set to 1 to dump all images to /tmp
- FWLIST = {
- # Deathadder 1.27
- "92d7f44637858405a83c0f192c61388c" : Descriptor(0x14B28, 0x1D8F4, IHEXParser, 0x4000)
- }
- def __init__(self, filepath):
- try:
- self.data = open(filepath, "rb").read()
- except IOError as e:
- raise RazerEx("Could not read file: %s" % e.strerror)
- md5sum = hashlib.md5(self.data).hexdigest().lower()
- try:
- descriptor = self.FWLIST[md5sum]
- except KeyError:
- raise RazerEx("Unsupported firmware file")
- try:
- rawFwData = self.data[descriptor.start : descriptor.start+descriptor.len]
- if self.DUMP:
- open("/tmp/razer.dump", "wb").write(rawFwData)
- fwImage = descriptor.parser(rawFwData).parse()
- if self.DUMP:
- open("/tmp/razer.dump.image", "wb").write(fwImage)
- if descriptor.binTruncate:
- fwImage = fwImage[:descriptor.binTruncate]
- if self.DUMP:
- open("/tmp/razer.dump.image.trunc", "wb").write(fwImage)
- except IndexError:
- raise RazerEx("Invalid firmware file format")
- self.fwImage = fwImage
- def getImage(self):
- return self.fwImage
|