brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 36409a5 Raw
375 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *   serial-generic.c4 *   Copyright (c) by Daniel Kaehn <kaehndan@gmail.com5 *   Based on serial-u16550.c by Jaroslav Kysela <perex@perex.cz>,6 *		                 Isaku Yamahata <yamahata@private.email.ne.jp>,7 *		                 George Hansper <ghansper@apana.org.au>,8 *		                 Hannu Savolainen9 *10 * Generic serial MIDI driver using the serdev serial bus API for hardware interaction11 */12 13#include <linux/err.h>14#include <linux/init.h>15#include <linux/interrupt.h>16#include <linux/io.h>17#include <linux/ioport.h>18#include <linux/module.h>19#include <linux/of.h>20#include <linux/serdev.h>21#include <linux/serial_reg.h>22#include <linux/slab.h>23#include <linux/dev_printk.h>24 25#include <sound/core.h>26#include <sound/rawmidi.h>27#include <sound/initval.h>28 29MODULE_DESCRIPTION("Generic serial MIDI driver");30MODULE_LICENSE("GPL");31 32#define SERIAL_MODE_INPUT_OPEN		133#define SERIAL_MODE_OUTPUT_OPEN	234#define SERIAL_MODE_INPUT_TRIGGERED	335#define SERIAL_MODE_OUTPUT_TRIGGERED	436 37#define SERIAL_TX_STATE_ACTIVE	138#define SERIAL_TX_STATE_WAKEUP	239 40struct snd_serial_generic {41	struct serdev_device *serdev;42 43	struct snd_card *card;44	struct snd_rawmidi *rmidi;45	struct snd_rawmidi_substream *midi_output;46	struct snd_rawmidi_substream *midi_input;47 48	unsigned int baudrate;49 50	unsigned long filemode;		/* open status of file */51	struct work_struct tx_work;52	unsigned long tx_state;53 54};55 56static void snd_serial_generic_tx_wakeup(struct snd_serial_generic *drvdata)57{58	if (test_and_set_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state))59		set_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);60 61	schedule_work(&drvdata->tx_work);62}63 64#define INTERNAL_BUF_SIZE 25665 66static void snd_serial_generic_tx_work(struct work_struct *work)67{68	static char buf[INTERNAL_BUF_SIZE];69	int num_bytes;70	struct snd_serial_generic *drvdata = container_of(work, struct snd_serial_generic,71						   tx_work);72	struct snd_rawmidi_substream *substream = drvdata->midi_output;73 74	clear_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);75 76	while (!snd_rawmidi_transmit_empty(substream)) {77 78		if (!test_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode))79			break;80 81		num_bytes = snd_rawmidi_transmit_peek(substream, buf, INTERNAL_BUF_SIZE);82		num_bytes = serdev_device_write_buf(drvdata->serdev, buf, num_bytes);83 84		if (!num_bytes)85			break;86 87		snd_rawmidi_transmit_ack(substream, num_bytes);88 89		if (!test_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state))90			break;91	}92 93	clear_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state);94}95 96static void snd_serial_generic_write_wakeup(struct serdev_device *serdev)97{98	struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);99 100	snd_serial_generic_tx_wakeup(drvdata);101}102 103static size_t snd_serial_generic_receive_buf(struct serdev_device *serdev,104					     const u8 *buf, size_t count)105{106	int ret;107	struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);108 109	if (!test_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode))110		return 0;111 112	ret = snd_rawmidi_receive(drvdata->midi_input, buf, count);113	return ret < 0 ? 0 : ret;114}115 116static const struct serdev_device_ops snd_serial_generic_serdev_device_ops = {117	.receive_buf = snd_serial_generic_receive_buf,118	.write_wakeup = snd_serial_generic_write_wakeup119};120 121static int snd_serial_generic_ensure_serdev_open(struct snd_serial_generic *drvdata)122{123	int err;124	unsigned int actual_baud;125 126	if (drvdata->filemode)127		return 0;128 129	dev_dbg(drvdata->card->dev, "Opening serial port for card %s\n",130		drvdata->card->shortname);131	err = serdev_device_open(drvdata->serdev);132	if (err < 0)133		return err;134 135	actual_baud = serdev_device_set_baudrate(drvdata->serdev,136		drvdata->baudrate);137	if (actual_baud != drvdata->baudrate) {138		dev_warn(drvdata->card->dev, "requested %d baud for card %s but it was actually set to %d\n",139			drvdata->baudrate, drvdata->card->shortname, actual_baud);140	}141 142	return 0;143}144 145static int snd_serial_generic_input_open(struct snd_rawmidi_substream *substream)146{147	int err;148	struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;149 150	dev_dbg(drvdata->card->dev, "Opening input for card %s\n",151		drvdata->card->shortname);152 153	err = snd_serial_generic_ensure_serdev_open(drvdata);154	if (err < 0)155		return err;156 157	set_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);158	drvdata->midi_input = substream;159	return 0;160}161 162static int snd_serial_generic_input_close(struct snd_rawmidi_substream *substream)163{164	struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;165 166	dev_dbg(drvdata->card->dev, "Closing input for card %s\n",167		drvdata->card->shortname);168 169	clear_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);170	clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);171 172	drvdata->midi_input = NULL;173 174	if (!drvdata->filemode)175		serdev_device_close(drvdata->serdev);176	return 0;177}178 179static void snd_serial_generic_input_trigger(struct snd_rawmidi_substream *substream,180					int up)181{182	struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;183 184	if (up)185		set_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);186	else187		clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);188}189 190static int snd_serial_generic_output_open(struct snd_rawmidi_substream *substream)191{192	struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;193	int err;194 195	dev_dbg(drvdata->card->dev, "Opening output for card %s\n",196		drvdata->card->shortname);197 198	err = snd_serial_generic_ensure_serdev_open(drvdata);199	if (err < 0)200		return err;201 202	set_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);203 204	drvdata->midi_output = substream;205	return 0;206};207 208static int snd_serial_generic_output_close(struct snd_rawmidi_substream *substream)209{210	struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;211 212	dev_dbg(drvdata->card->dev, "Closing output for card %s\n",213		drvdata->card->shortname);214 215	clear_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);216	clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);217 218	if (!drvdata->filemode)219		serdev_device_close(drvdata->serdev);220 221	drvdata->midi_output = NULL;222 223	return 0;224};225 226static void snd_serial_generic_output_trigger(struct snd_rawmidi_substream *substream,227					 int up)228{229	struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;230 231	if (up)232		set_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);233	else234		clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);235 236	if (up)237		snd_serial_generic_tx_wakeup(drvdata);238}239 240static void snd_serial_generic_output_drain(struct snd_rawmidi_substream *substream)241{242	struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;243 244	/* Flush any pending characters */245	serdev_device_write_flush(drvdata->serdev);246	cancel_work_sync(&drvdata->tx_work);247}248 249static const struct snd_rawmidi_ops snd_serial_generic_output = {250	.open =		snd_serial_generic_output_open,251	.close =	snd_serial_generic_output_close,252	.trigger =	snd_serial_generic_output_trigger,253	.drain =	snd_serial_generic_output_drain,254};255 256static const struct snd_rawmidi_ops snd_serial_generic_input = {257	.open =		snd_serial_generic_input_open,258	.close =	snd_serial_generic_input_close,259	.trigger =	snd_serial_generic_input_trigger,260};261 262static void snd_serial_generic_parse_dt(struct serdev_device *serdev,263				struct snd_serial_generic *drvdata)264{265	int err;266 267	err = of_property_read_u32(serdev->dev.of_node, "current-speed",268		&drvdata->baudrate);269	if (err < 0) {270		dev_dbg(drvdata->card->dev,271			"MIDI device reading of current-speed DT param failed with error %d, using default of 38400\n",272			err);273		drvdata->baudrate = 38400;274	}275 276}277 278static void snd_serial_generic_substreams(struct snd_rawmidi_str *stream, int dev_num)279{280	struct snd_rawmidi_substream *substream;281 282	list_for_each_entry(substream, &stream->substreams, list) {283		sprintf(substream->name, "Serial MIDI %d-%d", dev_num, substream->number);284	}285}286 287static int snd_serial_generic_rmidi(struct snd_serial_generic *drvdata,288				int outs, int ins, struct snd_rawmidi **rmidi)289{290	struct snd_rawmidi *rrawmidi;291	int err;292 293	err = snd_rawmidi_new(drvdata->card, drvdata->card->driver, 0,294				outs, ins, &rrawmidi);295 296	if (err < 0)297		return err;298 299	snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT,300				&snd_serial_generic_input);301	snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,302				&snd_serial_generic_output);303	strcpy(rrawmidi->name, drvdata->card->shortname);304 305	snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],306					drvdata->serdev->ctrl->nr);307	snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],308					drvdata->serdev->ctrl->nr);309 310	rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |311			       SNDRV_RAWMIDI_INFO_INPUT |312			       SNDRV_RAWMIDI_INFO_DUPLEX;313 314	if (rmidi)315		*rmidi = rrawmidi;316	return 0;317}318 319static int snd_serial_generic_probe(struct serdev_device *serdev)320{321	struct snd_card *card;322	struct snd_serial_generic *drvdata;323	int err;324 325	err  = snd_devm_card_new(&serdev->dev, SNDRV_DEFAULT_IDX1,326				SNDRV_DEFAULT_STR1, THIS_MODULE,327				sizeof(struct snd_serial_generic), &card);328 329	if (err < 0)330		return err;331 332	strcpy(card->driver, "SerialMIDI");333	sprintf(card->shortname, "SerialMIDI-%d", serdev->ctrl->nr);334	sprintf(card->longname, "Serial MIDI device at serial%d", serdev->ctrl->nr);335 336	drvdata = card->private_data;337 338	drvdata->serdev = serdev;339	drvdata->card = card;340 341	snd_serial_generic_parse_dt(serdev, drvdata);342 343	INIT_WORK(&drvdata->tx_work, snd_serial_generic_tx_work);344 345	err = snd_serial_generic_rmidi(drvdata, 1, 1, &drvdata->rmidi);346	if (err < 0)347		return err;348 349	serdev_device_set_client_ops(serdev, &snd_serial_generic_serdev_device_ops);350	serdev_device_set_drvdata(drvdata->serdev, drvdata);351 352	err = snd_card_register(card);353	if (err < 0)354		return err;355 356	return 0;357}358 359static const struct of_device_id snd_serial_generic_dt_ids[] = {360	{ .compatible = "serial-midi" },361	{},362};363 364MODULE_DEVICE_TABLE(of, snd_serial_generic_dt_ids);365 366static struct serdev_device_driver snd_serial_generic_driver = {367	.driver	= {368		.name		= "snd-serial-generic",369		.of_match_table	= snd_serial_generic_dt_ids,370	},371	.probe	= snd_serial_generic_probe,372};373 374module_serdev_device_driver(snd_serial_generic_driver);375