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