===== WARNING / ACHTUNG / 警告 ===== This page is intended solely as a space to jot notes onto. Do not make any decisions based on the content here. Some portions came from cursory reviews & may, in fact, be incorrect. ==== User requests ==== * open() - increments a channel's **refcount** field. (Only referenced when attempting to unload the device & detecting that channels are still open.) * close() - decrements a channels **refcount** field. * read(), write() * Channel lock acquired/held for duration of operation, except for a temporary release around uiomove(). * Parent PCM device's **inprog** counter incremented on entrance, decremented on exit. * poll(), mmap() - Channel lock acquired/held for duration of operation. === Ioctls === By default, no locking is performed. Typically, a channel lock is acquired in order to complete a few instructions, then is released. (E.g., acquire, set buffer size, release.) This is satisfactory in many cases, but operations which touch other channels make things a little tricky (ex: SNDCTL_AUDIOINFO). Another toughy are SNDCTL_DSP_SYNCGROUP and SNDCTL_DSP_SYNCSTART. Syncgroups are singly-linked lists of PCM channels set to begin playing at the same time. These lists are also kept in a singly linked list, since the number of entries is small and nearly all insert/remove operations are done at the head. The master list (and all member group lists) are protected by a single mutex, so it is important that a caller acquire this mutex first //without// any channel locks held. This is necessary for the SYNCSTART operation which iterates over all channels in a group, locks them, and begins playback/recording. Hack of a solution: in addition to a mutex, each channel maintains a conditional variable (**cv**) and in progress counter (**inprog**). For operations which might temporarily give up the mutex, **inprog** is incremented before releasing the mutex. Any other thread that acquires the channel lock should inspect **inprog** - if it is non-zero, that thread should call cv_wait() on the channel's lock & cv. (This is typically done in a loop.) When the owning thread reacquires the lock, it should decrement **inprog** and call cv_signal() on the lock & cv. Implementing that scheme would require updating channel.c::chn_lock() to include the cv handling, then updating CHN_LOCK/CHN_UNLOCK macros to make use of these checks. ==== Interrupts ==== - CHN_LOCK(channel) - chn_{wr,rd}intr() - CHN_UNLOCK(channel)