18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Functions for the OPL4 proc file
48c2ecf20Sopenharmony_ci * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "opl4_local.h"
88c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <sound/info.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic int snd_opl4_mem_proc_open(struct snd_info_entry *entry,
138c2ecf20Sopenharmony_ci				  unsigned short mode, void **file_private_data)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	mutex_lock(&opl4->access_mutex);
188c2ecf20Sopenharmony_ci	if (opl4->memory_access) {
198c2ecf20Sopenharmony_ci		mutex_unlock(&opl4->access_mutex);
208c2ecf20Sopenharmony_ci		return -EBUSY;
218c2ecf20Sopenharmony_ci	}
228c2ecf20Sopenharmony_ci	opl4->memory_access++;
238c2ecf20Sopenharmony_ci	mutex_unlock(&opl4->access_mutex);
248c2ecf20Sopenharmony_ci	return 0;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int snd_opl4_mem_proc_release(struct snd_info_entry *entry,
288c2ecf20Sopenharmony_ci				     unsigned short mode, void *file_private_data)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	mutex_lock(&opl4->access_mutex);
338c2ecf20Sopenharmony_ci	opl4->memory_access--;
348c2ecf20Sopenharmony_ci	mutex_unlock(&opl4->access_mutex);
358c2ecf20Sopenharmony_ci	return 0;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic ssize_t snd_opl4_mem_proc_read(struct snd_info_entry *entry,
398c2ecf20Sopenharmony_ci				      void *file_private_data,
408c2ecf20Sopenharmony_ci				      struct file *file, char __user *_buf,
418c2ecf20Sopenharmony_ci				      size_t count, loff_t pos)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
448c2ecf20Sopenharmony_ci	char* buf;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	buf = vmalloc(count);
478c2ecf20Sopenharmony_ci	if (!buf)
488c2ecf20Sopenharmony_ci		return -ENOMEM;
498c2ecf20Sopenharmony_ci	snd_opl4_read_memory(opl4, buf, pos, count);
508c2ecf20Sopenharmony_ci	if (copy_to_user(_buf, buf, count)) {
518c2ecf20Sopenharmony_ci		vfree(buf);
528c2ecf20Sopenharmony_ci		return -EFAULT;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci	vfree(buf);
558c2ecf20Sopenharmony_ci	return count;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic ssize_t snd_opl4_mem_proc_write(struct snd_info_entry *entry,
598c2ecf20Sopenharmony_ci				       void *file_private_data,
608c2ecf20Sopenharmony_ci				       struct file *file,
618c2ecf20Sopenharmony_ci				       const char __user *_buf,
628c2ecf20Sopenharmony_ci				       size_t count, loff_t pos)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
658c2ecf20Sopenharmony_ci	char *buf;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	buf = vmalloc(count);
688c2ecf20Sopenharmony_ci	if (!buf)
698c2ecf20Sopenharmony_ci		return -ENOMEM;
708c2ecf20Sopenharmony_ci	if (copy_from_user(buf, _buf, count)) {
718c2ecf20Sopenharmony_ci		vfree(buf);
728c2ecf20Sopenharmony_ci		return -EFAULT;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	snd_opl4_write_memory(opl4, buf, pos, count);
758c2ecf20Sopenharmony_ci	vfree(buf);
768c2ecf20Sopenharmony_ci	return count;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic const struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
808c2ecf20Sopenharmony_ci	.open = snd_opl4_mem_proc_open,
818c2ecf20Sopenharmony_ci	.release = snd_opl4_mem_proc_release,
828c2ecf20Sopenharmony_ci	.read = snd_opl4_mem_proc_read,
838c2ecf20Sopenharmony_ci	.write = snd_opl4_mem_proc_write,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciint snd_opl4_create_proc(struct snd_opl4 *opl4)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct snd_info_entry *entry;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	entry = snd_info_create_card_entry(opl4->card, "opl4-mem", opl4->card->proc_root);
918c2ecf20Sopenharmony_ci	if (entry) {
928c2ecf20Sopenharmony_ci		if (opl4->hardware < OPL3_HW_OPL4_ML) {
938c2ecf20Sopenharmony_ci			/* OPL4 can access 4 MB external ROM/SRAM */
948c2ecf20Sopenharmony_ci			entry->mode |= 0200;
958c2ecf20Sopenharmony_ci			entry->size = 4 * 1024 * 1024;
968c2ecf20Sopenharmony_ci		} else {
978c2ecf20Sopenharmony_ci			/* OPL4-ML has 1 MB internal ROM */
988c2ecf20Sopenharmony_ci			entry->size = 1 * 1024 * 1024;
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci		entry->content = SNDRV_INFO_CONTENT_DATA;
1018c2ecf20Sopenharmony_ci		entry->c.ops = &snd_opl4_mem_proc_ops;
1028c2ecf20Sopenharmony_ci		entry->module = THIS_MODULE;
1038c2ecf20Sopenharmony_ci		entry->private_data = opl4;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	opl4->proc_entry = entry;
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_civoid snd_opl4_free_proc(struct snd_opl4 *opl4)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	snd_info_free_entry(opl4->proc_entry);
1128c2ecf20Sopenharmony_ci}
113