diff options
-rw-r--r-- | rules/Makefile.am | 2 | ||||
-rw-r--r-- | rules/rules.d/60-persistent-alsa.rules | 15 | ||||
-rw-r--r-- | rules/rules.d/78-sound-card.rules | 91 |
3 files changed, 108 insertions, 0 deletions
diff --git a/rules/Makefile.am b/rules/Makefile.am index 6f1744fdb3..b1dc088e2c 100644 --- a/rules/Makefile.am +++ b/rules/Makefile.am @@ -7,6 +7,8 @@ dist_udevrules_DATA = \ rules.d/60-persistent-storage-tape.rules \ rules.d/60-persistent-serial.rules \ rules.d/60-persistent-input.rules \ + rules.d/60-persistent-alsa.rules \ + rules.d/78-sound-card.rules \ rules.d/80-drivers.rules \ rules.d/95-udev-late.rules diff --git a/rules/rules.d/60-persistent-alsa.rules b/rules/rules.d/60-persistent-alsa.rules new file mode 100644 index 0000000000..a5206fdbb1 --- /dev/null +++ b/rules/rules.d/60-persistent-alsa.rules @@ -0,0 +1,15 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add|change", GOTO="persistent_alsa_end" +SUBSYSTEM!="sound", GOTO="persistent_alsa_end" +KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end" + +SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{program}="usb_id --export %p" +SUBSYSTEMS=="usb", ENV{ID_IFACE}="$attr{bInterfaceNumber}" +ENV{ID_SERIAL}=="?*", ENV{ID_IFACE}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_IFACE}" +ENV{ID_SERIAL}=="?*", ENV{ID_IFACE}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}" + +ENV{ID_PATH}=="", IMPORT{program}="path_id %p" +ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}" + +LABEL="persistent_alsa_end" diff --git a/rules/rules.d/78-sound-card.rules b/rules/rules.d/78-sound-card.rules new file mode 100644 index 0000000000..f851b46e00 --- /dev/null +++ b/rules/rules.d/78-sound-card.rules @@ -0,0 +1,91 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="sound", GOTO="sound_end" + +ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change" +ACTION!="change", GOTO="sound_end" + +# Ok, we probably need a little explanation here for what the two lines above +# are good for. +# +# The story goes like this: when ALSA registers a new sound card it emits a +# series of 'add' events to userspace, for the main card device and for all the +# child device nodes that belong to it. udev relays those to applications, +# however only maintains the order between father and child, but not between +# the siblings. The control device node creation can be used as synchronization +# point. All other devices that belong to a card are created in the kernel +# before it. However unfortunately due to the fact that siblings are forwarded +# out of order by udev this fact is lost to applications. +# +# OTOH before an application can open a device it needs to make sure that all +# its device nodes are completely created and set up. +# +# As a workaround for this issue we have added the udev rule above which will +# generate a 'change' event on the main card device from the 'add' event of the +# card's control device. Due to the ordering semantics of udev this event will +# only be relayed after all child devices have finished processing properly. +# When an application needs to listen for appearing devices it can hence look +# for 'change' events only, and ignore the actual 'add' events. +# +# When the application is initialized at the same time as a device is plugged +# in it may need to figure out if the 'change' event has already been triggered +# or not for a card. To find that out we store the flag environment variable +# SOUND_INITIALIZED on the device which simply tells us if the card 'change' +# event has already been processed. + +KERNEL!="card*", GOTO="sound_end" + +ENV{SOUND_INITIALIZED}="1" + +SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{program}="usb_id --export %p" +SUBSYSTEMS=="usb", ENV{ID_VENDOR_FROM_DATABASE}=="", IMPORT{program}="usb-db %p" +SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}!="", ENV{ID_IFACE}="$attr{bInterfaceNumber}" +SUBSYSTEMS=="usb", GOTO="skip_pci" + +SUBSYSTEMS=="pci", ENV{ID_VENDOR_FROM_DATABASE}=="", IMPORT{program}="pci-db %p" +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" + +LABEL="skip_pci" + +ENV{ID_SERIAL}=="?*", ENV{ID_IFACE}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_IFACE}" +ENV{ID_SERIAL}=="?*", ENV{ID_IFACE}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}" + +ENV{ID_PATH}=="", IMPORT{program}="/usr/bin/env -i /lib/udev/path_id %p/controlC%n" + +# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept +# in sync with those defined for PulseAudio's src/pulse/proplist.h +# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties. + +# If the first PCM device of this card has the pcm class 'modem', then the card is a modem +ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end" + +# Identify cards on the internal PCI bus as internal +SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end" + +# Recognize good old WinTV cards as TV cards +SUBSYSTEMS=="pci", DRIVERS=="Bt87x", ENV{SOUND_FORM_FACTOR}="tv", GOTO="sound_end" + +# Hmm, do we really want this database here? +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0471", ATTRS{idProduct}=="0311", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" + +# Devices that also support Image/Video interfaces are most likely webcams +SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" + +# Matching on the model strings is a bit ugly, I admit +ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" + +LABEL="sound_end" |