162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Functions for the OPL4 proc file
462306a36Sopenharmony_ci * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "opl4_local.h"
862306a36Sopenharmony_ci#include <linux/vmalloc.h>
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <sound/info.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic int snd_opl4_mem_proc_open(struct snd_info_entry *entry,
1362306a36Sopenharmony_ci				  unsigned short mode, void **file_private_data)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	mutex_lock(&opl4->access_mutex);
1862306a36Sopenharmony_ci	if (opl4->memory_access) {
1962306a36Sopenharmony_ci		mutex_unlock(&opl4->access_mutex);
2062306a36Sopenharmony_ci		return -EBUSY;
2162306a36Sopenharmony_ci	}
2262306a36Sopenharmony_ci	opl4->memory_access++;
2362306a36Sopenharmony_ci	mutex_unlock(&opl4->access_mutex);
2462306a36Sopenharmony_ci	return 0;
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int snd_opl4_mem_proc_release(struct snd_info_entry *entry,
2862306a36Sopenharmony_ci				     unsigned short mode, void *file_private_data)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	mutex_lock(&opl4->access_mutex);
3362306a36Sopenharmony_ci	opl4->memory_access--;
3462306a36Sopenharmony_ci	mutex_unlock(&opl4->access_mutex);
3562306a36Sopenharmony_ci	return 0;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic ssize_t snd_opl4_mem_proc_read(struct snd_info_entry *entry,
3962306a36Sopenharmony_ci				      void *file_private_data,
4062306a36Sopenharmony_ci				      struct file *file, char __user *_buf,
4162306a36Sopenharmony_ci				      size_t count, loff_t pos)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
4462306a36Sopenharmony_ci	char* buf;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	buf = vmalloc(count);
4762306a36Sopenharmony_ci	if (!buf)
4862306a36Sopenharmony_ci		return -ENOMEM;
4962306a36Sopenharmony_ci	snd_opl4_read_memory(opl4, buf, pos, count);
5062306a36Sopenharmony_ci	if (copy_to_user(_buf, buf, count)) {
5162306a36Sopenharmony_ci		vfree(buf);
5262306a36Sopenharmony_ci		return -EFAULT;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	vfree(buf);
5562306a36Sopenharmony_ci	return count;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic ssize_t snd_opl4_mem_proc_write(struct snd_info_entry *entry,
5962306a36Sopenharmony_ci				       void *file_private_data,
6062306a36Sopenharmony_ci				       struct file *file,
6162306a36Sopenharmony_ci				       const char __user *_buf,
6262306a36Sopenharmony_ci				       size_t count, loff_t pos)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct snd_opl4 *opl4 = entry->private_data;
6562306a36Sopenharmony_ci	char *buf;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	buf = vmalloc(count);
6862306a36Sopenharmony_ci	if (!buf)
6962306a36Sopenharmony_ci		return -ENOMEM;
7062306a36Sopenharmony_ci	if (copy_from_user(buf, _buf, count)) {
7162306a36Sopenharmony_ci		vfree(buf);
7262306a36Sopenharmony_ci		return -EFAULT;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci	snd_opl4_write_memory(opl4, buf, pos, count);
7562306a36Sopenharmony_ci	vfree(buf);
7662306a36Sopenharmony_ci	return count;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic const struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
8062306a36Sopenharmony_ci	.open = snd_opl4_mem_proc_open,
8162306a36Sopenharmony_ci	.release = snd_opl4_mem_proc_release,
8262306a36Sopenharmony_ci	.read = snd_opl4_mem_proc_read,
8362306a36Sopenharmony_ci	.write = snd_opl4_mem_proc_write,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint snd_opl4_create_proc(struct snd_opl4 *opl4)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct snd_info_entry *entry;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	entry = snd_info_create_card_entry(opl4->card, "opl4-mem", opl4->card->proc_root);
9162306a36Sopenharmony_ci	if (entry) {
9262306a36Sopenharmony_ci		if (opl4->hardware < OPL3_HW_OPL4_ML) {
9362306a36Sopenharmony_ci			/* OPL4 can access 4 MB external ROM/SRAM */
9462306a36Sopenharmony_ci			entry->mode |= 0200;
9562306a36Sopenharmony_ci			entry->size = 4 * 1024 * 1024;
9662306a36Sopenharmony_ci		} else {
9762306a36Sopenharmony_ci			/* OPL4-ML has 1 MB internal ROM */
9862306a36Sopenharmony_ci			entry->size = 1 * 1024 * 1024;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci		entry->content = SNDRV_INFO_CONTENT_DATA;
10162306a36Sopenharmony_ci		entry->c.ops = &snd_opl4_mem_proc_ops;
10262306a36Sopenharmony_ci		entry->module = THIS_MODULE;
10362306a36Sopenharmony_ci		entry->private_data = opl4;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci	opl4->proc_entry = entry;
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_civoid snd_opl4_free_proc(struct snd_opl4 *opl4)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	snd_info_free_entry(opl4->proc_entry);
11262306a36Sopenharmony_ci}
113