sound/firewire/fireworks/fireworks_hwdep.c

Source file repositories/reference/linux-study-clean/sound/firewire/fireworks/fireworks_hwdep.c

File Facts

System
Linux kernel
Corpus path
sound/firewire/fireworks/fireworks_hwdep.c
Extension
.c
Size
7356 bytes
Lines
314
Domain
Driver Families
Bucket
sound/firewire
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

while (length > 0) {
			till_end = snd_efw_resp_buf_size -
				(unsigned int)(pull_ptr - efw->resp_buf);
			till_end = min_t(unsigned int, length, till_end);

			spin_unlock_irq(&efw->lock);

			if (copy_to_user(buf, pull_ptr, till_end))
				return -EFAULT;

			spin_lock_irq(&efw->lock);

			pull_ptr += till_end;
			if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
				pull_ptr -= snd_efw_resp_buf_size;

			length -= till_end;
			buf += till_end;
			count += till_end;
			remained -= till_end;
		}
	}

	/*
	 * All of tasks can read from the buffer nearly simultaneously, but the
	 * last position for each task is different depending on the length of
	 * given buffer. Here, for simplicity, a position of buffer is set by
	 * the latest task. It's better for a listening application to allow one
	 * thread to read from the buffer. Unless, each task can read different
	 * sequence of responses depending on variation of buffer length.
	 */
	efw->pull_ptr = pull_ptr;

	spin_unlock_irq(&efw->lock);

	return count;
}

static long
hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
		  loff_t *offset)
{
	union snd_firewire_event event = {
		.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
	};

	scoped_guard(spinlock_irq, &efw->lock) {
		event.lock_status.status = (efw->dev_lock_count > 0);
		efw->dev_lock_changed = false;
	}

	count = min_t(long, count, sizeof(event.lock_status));

	if (copy_to_user(buf, &event, count))
		return -EFAULT;

	return count;
}

static long
hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
	   loff_t *offset)
{
	struct snd_efw *efw = hwdep->private_data;
	DEFINE_WAIT(wait);
	bool dev_lock_changed;
	bool queued;

	spin_lock_irq(&efw->lock);

	dev_lock_changed = efw->dev_lock_changed;
	queued = efw->push_ptr != efw->pull_ptr;

	while (!dev_lock_changed && !queued) {
		prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
		spin_unlock_irq(&efw->lock);
		schedule();
		finish_wait(&efw->hwdep_wait, &wait);
		if (signal_pending(current))
			return -ERESTARTSYS;
		spin_lock_irq(&efw->lock);
		dev_lock_changed = efw->dev_lock_changed;
		queued = efw->push_ptr != efw->pull_ptr;
	}

	spin_unlock_irq(&efw->lock);

	if (dev_lock_changed)
		count = hwdep_read_locked(efw, buf, count, offset);
	else if (queued)

Annotation

Implementation Notes