xref: /kernel/linux/linux-5.10/sound/usb/6fire/comm.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux driver for TerraTec DMX 6Fire USB
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Device communications
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author:	Torsten Schenk <torsten.schenk@zoho.com>
88c2ecf20Sopenharmony_ci * Created:	Jan 01, 2011
98c2ecf20Sopenharmony_ci * Copyright:	(C) Torsten Schenk
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "comm.h"
138c2ecf20Sopenharmony_ci#include "chip.h"
148c2ecf20Sopenharmony_ci#include "midi.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cienum {
178c2ecf20Sopenharmony_ci	COMM_EP = 1,
188c2ecf20Sopenharmony_ci	COMM_FPGA_EP = 2
198c2ecf20Sopenharmony_ci};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
228c2ecf20Sopenharmony_ci		u8 *buffer, void *context, void(*handler)(struct urb *urb))
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	usb_init_urb(urb);
258c2ecf20Sopenharmony_ci	urb->transfer_buffer = buffer;
268c2ecf20Sopenharmony_ci	urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
278c2ecf20Sopenharmony_ci	urb->complete = handler;
288c2ecf20Sopenharmony_ci	urb->context = context;
298c2ecf20Sopenharmony_ci	urb->interval = 1;
308c2ecf20Sopenharmony_ci	urb->dev = rt->chip->dev;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic void usb6fire_comm_receiver_handler(struct urb *urb)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct comm_runtime *rt = urb->context;
368c2ecf20Sopenharmony_ci	struct midi_runtime *midi_rt = rt->chip->midi;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (!urb->status) {
398c2ecf20Sopenharmony_ci		if (rt->receiver_buffer[0] == 0x10) /* midi in event */
408c2ecf20Sopenharmony_ci			if (midi_rt)
418c2ecf20Sopenharmony_ci				midi_rt->in_received(midi_rt,
428c2ecf20Sopenharmony_ci						rt->receiver_buffer + 2,
438c2ecf20Sopenharmony_ci						rt->receiver_buffer[1]);
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!rt->chip->shutdown) {
478c2ecf20Sopenharmony_ci		urb->status = 0;
488c2ecf20Sopenharmony_ci		urb->actual_length = 0;
498c2ecf20Sopenharmony_ci		if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
508c2ecf20Sopenharmony_ci			dev_warn(&urb->dev->dev,
518c2ecf20Sopenharmony_ci					"comm data receiver aborted.\n");
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request,
568c2ecf20Sopenharmony_ci		u8 reg, u8 vl, u8 vh)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	buffer[0] = 0x01;
598c2ecf20Sopenharmony_ci	buffer[2] = request;
608c2ecf20Sopenharmony_ci	buffer[3] = id;
618c2ecf20Sopenharmony_ci	switch (request) {
628c2ecf20Sopenharmony_ci	case 0x02:
638c2ecf20Sopenharmony_ci		buffer[1] = 0x05; /* length (starting at buffer[2]) */
648c2ecf20Sopenharmony_ci		buffer[4] = reg;
658c2ecf20Sopenharmony_ci		buffer[5] = vl;
668c2ecf20Sopenharmony_ci		buffer[6] = vh;
678c2ecf20Sopenharmony_ci		break;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	case 0x12:
708c2ecf20Sopenharmony_ci		buffer[1] = 0x0b; /* length (starting at buffer[2]) */
718c2ecf20Sopenharmony_ci		buffer[4] = 0x00;
728c2ecf20Sopenharmony_ci		buffer[5] = 0x18;
738c2ecf20Sopenharmony_ci		buffer[6] = 0x05;
748c2ecf20Sopenharmony_ci		buffer[7] = 0x00;
758c2ecf20Sopenharmony_ci		buffer[8] = 0x01;
768c2ecf20Sopenharmony_ci		buffer[9] = 0x00;
778c2ecf20Sopenharmony_ci		buffer[10] = 0x9e;
788c2ecf20Sopenharmony_ci		buffer[11] = reg;
798c2ecf20Sopenharmony_ci		buffer[12] = vl;
808c2ecf20Sopenharmony_ci		break;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	case 0x20:
838c2ecf20Sopenharmony_ci	case 0x21:
848c2ecf20Sopenharmony_ci	case 0x22:
858c2ecf20Sopenharmony_ci		buffer[1] = 0x04;
868c2ecf20Sopenharmony_ci		buffer[4] = reg;
878c2ecf20Sopenharmony_ci		buffer[5] = vl;
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	int ret;
958c2ecf20Sopenharmony_ci	int actual_len;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
988c2ecf20Sopenharmony_ci			buffer, buffer[1] + 2, &actual_len, 1000);
998c2ecf20Sopenharmony_ci	if (ret < 0)
1008c2ecf20Sopenharmony_ci		return ret;
1018c2ecf20Sopenharmony_ci	else if (actual_len != buffer[1] + 2)
1028c2ecf20Sopenharmony_ci		return -EIO;
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int usb6fire_comm_write8(struct comm_runtime *rt, u8 request,
1078c2ecf20Sopenharmony_ci		u8 reg, u8 value)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u8 *buffer;
1108c2ecf20Sopenharmony_ci	int ret;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* 13: maximum length of message */
1138c2ecf20Sopenharmony_ci	buffer = kmalloc(13, GFP_KERNEL);
1148c2ecf20Sopenharmony_ci	if (!buffer)
1158c2ecf20Sopenharmony_ci		return -ENOMEM;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00);
1188c2ecf20Sopenharmony_ci	ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	kfree(buffer);
1218c2ecf20Sopenharmony_ci	return ret;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
1258c2ecf20Sopenharmony_ci		u8 reg, u8 vl, u8 vh)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	u8 *buffer;
1288c2ecf20Sopenharmony_ci	int ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* 13: maximum length of message */
1318c2ecf20Sopenharmony_ci	buffer = kmalloc(13, GFP_KERNEL);
1328c2ecf20Sopenharmony_ci	if (!buffer)
1338c2ecf20Sopenharmony_ci		return -ENOMEM;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh);
1368c2ecf20Sopenharmony_ci	ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	kfree(buffer);
1398c2ecf20Sopenharmony_ci	return ret;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciint usb6fire_comm_init(struct sfire_chip *chip)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime),
1458c2ecf20Sopenharmony_ci			GFP_KERNEL);
1468c2ecf20Sopenharmony_ci	struct urb *urb;
1478c2ecf20Sopenharmony_ci	int ret;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (!rt)
1508c2ecf20Sopenharmony_ci		return -ENOMEM;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL);
1538c2ecf20Sopenharmony_ci	if (!rt->receiver_buffer) {
1548c2ecf20Sopenharmony_ci		kfree(rt);
1558c2ecf20Sopenharmony_ci		return -ENOMEM;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	urb = &rt->receiver;
1598c2ecf20Sopenharmony_ci	rt->serial = 1;
1608c2ecf20Sopenharmony_ci	rt->chip = chip;
1618c2ecf20Sopenharmony_ci	usb_init_urb(urb);
1628c2ecf20Sopenharmony_ci	rt->init_urb = usb6fire_comm_init_urb;
1638c2ecf20Sopenharmony_ci	rt->write8 = usb6fire_comm_write8;
1648c2ecf20Sopenharmony_ci	rt->write16 = usb6fire_comm_write16;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* submit an urb that receives communication data from device */
1678c2ecf20Sopenharmony_ci	urb->transfer_buffer = rt->receiver_buffer;
1688c2ecf20Sopenharmony_ci	urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE;
1698c2ecf20Sopenharmony_ci	urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP);
1708c2ecf20Sopenharmony_ci	urb->dev = chip->dev;
1718c2ecf20Sopenharmony_ci	urb->complete = usb6fire_comm_receiver_handler;
1728c2ecf20Sopenharmony_ci	urb->context = rt;
1738c2ecf20Sopenharmony_ci	urb->interval = 1;
1748c2ecf20Sopenharmony_ci	ret = usb_submit_urb(urb, GFP_KERNEL);
1758c2ecf20Sopenharmony_ci	if (ret < 0) {
1768c2ecf20Sopenharmony_ci		kfree(rt->receiver_buffer);
1778c2ecf20Sopenharmony_ci		kfree(rt);
1788c2ecf20Sopenharmony_ci		dev_err(&chip->dev->dev, "cannot create comm data receiver.");
1798c2ecf20Sopenharmony_ci		return ret;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	chip->comm = rt;
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_civoid usb6fire_comm_abort(struct sfire_chip *chip)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct comm_runtime *rt = chip->comm;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (rt)
1908c2ecf20Sopenharmony_ci		usb_poison_urb(&rt->receiver);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_civoid usb6fire_comm_destroy(struct sfire_chip *chip)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct comm_runtime *rt = chip->comm;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	kfree(rt->receiver_buffer);
1988c2ecf20Sopenharmony_ci	kfree(rt);
1998c2ecf20Sopenharmony_ci	chip->comm = NULL;
2008c2ecf20Sopenharmony_ci}
201