1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Device communications
5 *
6 * Author:	Torsten Schenk <torsten.schenk@zoho.com>
7 * Created:	Jan 01, 2011
8 * Copyright:	(C) Torsten Schenk
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
15
16#include "comm.h"
17#include "chip.h"
18#include "midi.h"
19
20enum {
21	COMM_EP = 1,
22	COMM_FPGA_EP = 2
23};
24
25static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
26		u8 *buffer, void *context, void(*handler)(struct urb *urb))
27{
28	usb_init_urb(urb);
29	urb->transfer_buffer = buffer;
30	urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
31	urb->complete = handler;
32	urb->context = context;
33	urb->interval = 1;
34	urb->dev = rt->chip->dev;
35}
36
37static void usb6fire_comm_receiver_handler(struct urb *urb)
38{
39	struct comm_runtime *rt = urb->context;
40	struct midi_runtime *midi_rt = rt->chip->midi;
41
42	if (!urb->status) {
43		if (rt->receiver_buffer[0] == 0x10) /* midi in event */
44			if (midi_rt)
45				midi_rt->in_received(midi_rt,
46						rt->receiver_buffer + 2,
47						rt->receiver_buffer[1]);
48	}
49
50	if (!rt->chip->shutdown) {
51		urb->status = 0;
52		urb->actual_length = 0;
53		if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
54			dev_warn(&urb->dev->dev,
55					"comm data receiver aborted.\n");
56	}
57}
58
59static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request,
60		u8 reg, u8 vl, u8 vh)
61{
62	buffer[0] = 0x01;
63	buffer[2] = request;
64	buffer[3] = id;
65	switch (request) {
66	case 0x02:
67		buffer[1] = 0x05; /* length (starting at buffer[2]) */
68		buffer[4] = reg;
69		buffer[5] = vl;
70		buffer[6] = vh;
71		break;
72
73	case 0x12:
74		buffer[1] = 0x0b; /* length (starting at buffer[2]) */
75		buffer[4] = 0x00;
76		buffer[5] = 0x18;
77		buffer[6] = 0x05;
78		buffer[7] = 0x00;
79		buffer[8] = 0x01;
80		buffer[9] = 0x00;
81		buffer[10] = 0x9e;
82		buffer[11] = reg;
83		buffer[12] = vl;
84		break;
85
86	case 0x20:
87	case 0x21:
88	case 0x22:
89		buffer[1] = 0x04;
90		buffer[4] = reg;
91		buffer[5] = vl;
92		break;
93	}
94}
95
96static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
97{
98	int ret;
99	int actual_len;
100
101	ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
102			buffer, buffer[1] + 2, &actual_len, HZ);
103	if (ret < 0)
104		return ret;
105	else if (actual_len != buffer[1] + 2)
106		return -EIO;
107	return 0;
108}
109
110static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request,
111		u8 reg, u8 value)
112{
113	u8 *buffer;
114	int ret;
115
116	/* 13: maximum length of message */
117	buffer = kmalloc(13, GFP_KERNEL);
118	if (!buffer)
119		return -ENOMEM;
120
121	usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00);
122	ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev);
123
124	kfree(buffer);
125	return ret;
126}
127
128static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
129		u8 reg, u8 vl, u8 vh)
130{
131	u8 *buffer;
132	int ret;
133
134	/* 13: maximum length of message */
135	buffer = kmalloc(13, GFP_KERNEL);
136	if (!buffer)
137		return -ENOMEM;
138
139	usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh);
140	ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev);
141
142	kfree(buffer);
143	return ret;
144}
145
146int usb6fire_comm_init(struct sfire_chip *chip)
147{
148	struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime),
149			GFP_KERNEL);
150	struct urb *urb;
151	int ret;
152
153	if (!rt)
154		return -ENOMEM;
155
156	rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL);
157	if (!rt->receiver_buffer) {
158		kfree(rt);
159		return -ENOMEM;
160	}
161
162	urb = &rt->receiver;
163	rt->serial = 1;
164	rt->chip = chip;
165	usb_init_urb(urb);
166	rt->init_urb = usb6fire_comm_init_urb;
167	rt->write8 = usb6fire_comm_write8;
168	rt->write16 = usb6fire_comm_write16;
169
170	/* submit an urb that receives communication data from device */
171	urb->transfer_buffer = rt->receiver_buffer;
172	urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE;
173	urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP);
174	urb->dev = chip->dev;
175	urb->complete = usb6fire_comm_receiver_handler;
176	urb->context = rt;
177	urb->interval = 1;
178	ret = usb_submit_urb(urb, GFP_KERNEL);
179	if (ret < 0) {
180		kfree(rt->receiver_buffer);
181		kfree(rt);
182		dev_err(&chip->dev->dev, "cannot create comm data receiver.");
183		return ret;
184	}
185	chip->comm = rt;
186	return 0;
187}
188
189void usb6fire_comm_abort(struct sfire_chip *chip)
190{
191	struct comm_runtime *rt = chip->comm;
192
193	if (rt)
194		usb_poison_urb(&rt->receiver);
195}
196
197void usb6fire_comm_destroy(struct sfire_chip *chip)
198{
199	struct comm_runtime *rt = chip->comm;
200
201	kfree(rt->receiver_buffer);
202	kfree(rt);
203	chip->comm = NULL;
204}
205