$ cat udev-rules-d.txt
###############################################################################
# My controller(s) setup
# - sairuk
#
# Save to `/etc/udev/rules.d` for use.
#
# Goal(s):
# -----------------------------------------------------------------------------
# 1. Where possible use udev
#
# 2. Provide static names for USB controllers regardless of port or order the
# devices are plugged into the system
#
# 3. Devices should be available when a controller is plugged in and any
# properties removed when the device is no longer connected
#
# Problem(s):
# -----------------------------------------------------------------------------
# Linux indexes the js devices by the order in which they are connected OR the
# order in which the device appears on the usb bus. This does not allow for
# static identifcation of a device because there is the possibility it will
# change on boot or reconnection.
#
# Multiplayer USB adapters do not provide properties where each player can
# be confidently identified by udev.
#
# Therefore using SYMLINK+="input/p%n" with KERNEL=="js[0-9*" or similar
# will not permit you create consistent device naming. The resulting name will
# be either:
# - determined by the device index
# - only create the initial symlink
# - overwrite the existing the symlink
#
# Using the udev RUN= or RUN+= to execute and external script will fire too
# quickly for the shell to process actions and you end up with a race condition
# when determining if a symlink exists for each player
#
# RUN does not appear to accept wildcards
#
# Solution
# -----------------------------------------------------------------------------
# When a device is added it is indexed from the first player through to the
# last therefore all incrementing indexes for a device should start at player1
# as lowest device index. We should be able to loop over the js devices and
# match on properties to determine the type of adapter then force our naming
# convention for symlinks
#
# Where it is not possible to accurately identify a each indiviual player on
# an adapter use an external script to create symlinks processesed outside of
# udev to meet goal 2
#
# let udev clean up the device to meet goals 1 and 3
#
###############################################################################
###############################################################################
# UDEV
#
# udev is used where a device has a property to allow for it to be identified
# accuractely and actioned appropriately. Perhaps the adapter is a single
# player adapter or has a property we can use to identify it.
#
# e.g. XBOX Dragon USB Adapter or a DS3 controller where we can match on the
# uniq device property.
###############################################################################
### XBOX Dragon USB Adapter
# REQUIRES: None
# SERIAL: 0c12_0005
# LSUSB: Bus 002 Device 072: ID 0c12:8801 Zeroplus Xbox Controller
ACTION=="add", SUBSYSTEM=="input", ATTRS{idVendor}=="0c12", GROUP="input", MODE="0660"
KERNEL=="js[0-9]*", SUBSYSTEM=="input", ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="0005", SYMLINK+="input/arcade"
### DUALSHOCK 3
#KERNEL=="event*", SUBSYSTEM=="input", ATTRS{uniq}=="00:1b:fb:Bbd:0b:bd", SYMLINK+="input/dualshock3"
###############################################################################
# EXTERNAL
#
# These USB adapters do not have a property available to determine which player
# the device is for so we handle that externally once the devices are connected
#
# The script /usr/local/bin/linkdev_js is run as a systemd service file by a
# systemd timer the timer executes the service every 5s
#
# systemd unit files
# /etc/systemd/system/controllers.service
# /etc/systemd/system/controllers.timer
#
# linkdev_js loops over the js devices checking if a symlink exists with a
# predetermined naming convention based on the SERIAL property of the device
#
# udev handles the symlink cleanup when the device is removed this is completed
# by the external script /usr/local/bin/linkdev_rm
#
###############################################################################
### TWINUSB
# REQUIRES: external script for symlinks
# SERIAL: 0810_Twin_USB_Joystick
# LSUSB: Bus 002 Device 005: ID 0810:0001 Personal Communication Systems, Inc. Dual PSX Adaptor
ACTION=="add", SUBSYSTEM=="input", ATTRS{idVendor}=="0810", GROUP="input", MODE="0660"
ACTION=="remove", SUBSYSTEM=="input", ATTRS{idVendor}=="0810", RUN+="/usr/local/bin/linkdev_rm psx"
### SNES 2p Adapter
# REQUIRES: external script for symlinks
# SERIAL: HuiJia_USB_GamePad
# LSUSB: Bus 002 Device 015: ID 0e8f:3013 GreenAsia Inc.
ACTION=="add", SUBSYSTEM=="input", ATTRS{idVendor}=="0e8f", GROUP="input", MODE="0660"
ACTION=="remove", SUBSYSTEM=="input", ATTRS{idVendor}=="0e8f", RUN+="/usr/local/bin/linkdev_rm snes"
### Gamecube 4p Adapter
# REQUIRES: external script for symlinks
# SERIAL: ShanWan_Hyperkin_4_Port_Adapter
# LSUSB: Bus 002 Device 014: ID 0079:1843 DragonRise Inc.
ACTION=="add", SUBSYSTEM=="input", ATTRS{idVendor}=="0079", GROUP="input", MODE="0660"
ACTION=="remove", SUBSYSTEM=="input", ATTRS{idVendor}=="0079", RUN+="/usr/local/bin/linkdev_rm gcp"