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