brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 74eed95 Raw
278 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tascam-hwdep.c - a part of driver for TASCAM FireWire series4 *5 * Copyright (c) 2015 Takashi Sakamoto6 */7 8/*9 * This codes give three functionality.10 *11 * 1.get firewire node information12 * 2.get notification about starting/stopping stream13 * 3.lock/unlock stream14 */15 16#include "tascam.h"17 18static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,19				   long count, loff_t *offset)20	__releases(&tscm->lock)21{22	struct snd_firewire_event_lock_status event = {23		.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,24	};25 26	event.status = (tscm->dev_lock_count > 0);27	tscm->dev_lock_changed = false;28	count = min_t(long, count, sizeof(event));29 30	spin_unlock_irq(&tscm->lock);31 32	if (copy_to_user(buf, &event, count))33		return -EFAULT;34 35	return count;36}37 38static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,39				  long remained, loff_t *offset)40	__releases(&tscm->lock)41{42	char __user *pos = buf;43	unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;44	struct snd_firewire_tascam_change *entries = tscm->queue;45	long count;46 47	// At least, one control event can be copied.48	if (remained < sizeof(type) + sizeof(*entries)) {49		spin_unlock_irq(&tscm->lock);50		return -EINVAL;51	}52 53	// Copy the type field later.54	count = sizeof(type);55	remained -= sizeof(type);56	pos += sizeof(type);57 58	while (true) {59		unsigned int head_pos;60		unsigned int tail_pos;61		unsigned int length;62 63		if (tscm->pull_pos == tscm->push_pos)64			break;65		else if (tscm->pull_pos < tscm->push_pos)66			tail_pos = tscm->push_pos;67		else68			tail_pos = SND_TSCM_QUEUE_COUNT;69		head_pos = tscm->pull_pos;70 71		length = (tail_pos - head_pos) * sizeof(*entries);72		if (remained < length)73			length = rounddown(remained, sizeof(*entries));74		if (length == 0)75			break;76 77		spin_unlock_irq(&tscm->lock);78		if (copy_to_user(pos, &entries[head_pos], length))79			return -EFAULT;80 81		spin_lock_irq(&tscm->lock);82 83		tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;84 85		count += length;86		remained -= length;87		pos += length;88	}89 90	spin_unlock_irq(&tscm->lock);91 92	if (copy_to_user(buf, &type, sizeof(type)))93		return -EFAULT;94 95	return count;96}97 98static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,99		       loff_t *offset)100{101	struct snd_tscm *tscm = hwdep->private_data;102	DEFINE_WAIT(wait);103 104	spin_lock_irq(&tscm->lock);105 106	while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {107		prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);108		spin_unlock_irq(&tscm->lock);109		schedule();110		finish_wait(&tscm->hwdep_wait, &wait);111		if (signal_pending(current))112			return -ERESTARTSYS;113		spin_lock_irq(&tscm->lock);114	}115 116	// NOTE: The acquired lock should be released in callee side.117	if (tscm->dev_lock_changed) {118		count = tscm_hwdep_read_locked(tscm, buf, count, offset);119	} else if (tscm->push_pos != tscm->pull_pos) {120		count = tscm_hwdep_read_queue(tscm, buf, count, offset);121	} else {122		spin_unlock_irq(&tscm->lock);123		count = 0;124	}125 126	return count;127}128 129static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,130			       poll_table *wait)131{132	struct snd_tscm *tscm = hwdep->private_data;133	__poll_t events;134 135	poll_wait(file, &tscm->hwdep_wait, wait);136 137	spin_lock_irq(&tscm->lock);138	if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)139		events = EPOLLIN | EPOLLRDNORM;140	else141		events = 0;142	spin_unlock_irq(&tscm->lock);143 144	return events;145}146 147static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)148{149	struct fw_device *dev = fw_parent_device(tscm->unit);150	struct snd_firewire_get_info info;151 152	memset(&info, 0, sizeof(info));153	info.type = SNDRV_FIREWIRE_TYPE_TASCAM;154	info.card = dev->card->index;155	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);156	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);157	strscpy(info.device_name, dev_name(&dev->device),158		sizeof(info.device_name));159 160	if (copy_to_user(arg, &info, sizeof(info)))161		return -EFAULT;162 163	return 0;164}165 166static int hwdep_lock(struct snd_tscm *tscm)167{168	int err;169 170	spin_lock_irq(&tscm->lock);171 172	if (tscm->dev_lock_count == 0) {173		tscm->dev_lock_count = -1;174		err = 0;175	} else {176		err = -EBUSY;177	}178 179	spin_unlock_irq(&tscm->lock);180 181	return err;182}183 184static int hwdep_unlock(struct snd_tscm *tscm)185{186	int err;187 188	spin_lock_irq(&tscm->lock);189 190	if (tscm->dev_lock_count == -1) {191		tscm->dev_lock_count = 0;192		err = 0;193	} else {194		err = -EBADFD;195	}196 197	spin_unlock_irq(&tscm->lock);198 199	return err;200}201 202static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg)203{204	if (copy_to_user(arg, tscm->state, sizeof(tscm->state)))205		return -EFAULT;206 207	return 0;208}209 210static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)211{212	struct snd_tscm *tscm = hwdep->private_data;213 214	spin_lock_irq(&tscm->lock);215	if (tscm->dev_lock_count == -1)216		tscm->dev_lock_count = 0;217	spin_unlock_irq(&tscm->lock);218 219	return 0;220}221 222static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,223	    unsigned int cmd, unsigned long arg)224{225	struct snd_tscm *tscm = hwdep->private_data;226 227	switch (cmd) {228	case SNDRV_FIREWIRE_IOCTL_GET_INFO:229		return hwdep_get_info(tscm, (void __user *)arg);230	case SNDRV_FIREWIRE_IOCTL_LOCK:231		return hwdep_lock(tscm);232	case SNDRV_FIREWIRE_IOCTL_UNLOCK:233		return hwdep_unlock(tscm);234	case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE:235		return tscm_hwdep_state(tscm, (void __user *)arg);236	default:237		return -ENOIOCTLCMD;238	}239}240 241#ifdef CONFIG_COMPAT242static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,243			      unsigned int cmd, unsigned long arg)244{245	return hwdep_ioctl(hwdep, file, cmd,246			   (unsigned long)compat_ptr(arg));247}248#else249#define hwdep_compat_ioctl NULL250#endif251 252int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)253{254	static const struct snd_hwdep_ops ops = {255		.read		= hwdep_read,256		.release	= hwdep_release,257		.poll		= hwdep_poll,258		.ioctl		= hwdep_ioctl,259		.ioctl_compat	= hwdep_compat_ioctl,260	};261	struct snd_hwdep *hwdep;262	int err;263 264	err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);265	if (err < 0)266		return err;267 268	strcpy(hwdep->name, "Tascam");269	hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;270	hwdep->ops = ops;271	hwdep->private_data = tscm;272	hwdep->exclusive = true;273 274	tscm->hwdep = hwdep;275 276	return err;277}278