1 | // SPDX-License-Identifier: GPL-2.0-or-later |
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 |
9 | * Copyright: (C) Torsten Schenk |
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) |
50 | dev_warn(&urb->dev->dev, |
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(usb_dev: dev, usb_sndintpipe(dev, COMM_EP), |
98 | data: buffer, len: buffer[1] + 2, actual_length: &actual_len, timeout: 1000); |
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 | { |
109 | u8 *buffer; |
110 | int ret; |
111 | |
112 | /* 13: maximum length of message */ |
113 | buffer = kmalloc(size: 13, GFP_KERNEL); |
114 | if (!buffer) |
115 | return -ENOMEM; |
116 | |
117 | usb6fire_comm_init_buffer(buffer, id: 0x00, request, reg, vl: value, vh: 0x00); |
118 | ret = usb6fire_comm_send_buffer(buffer, dev: rt->chip->dev); |
119 | |
120 | kfree(objp: buffer); |
121 | return ret; |
122 | } |
123 | |
124 | static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request, |
125 | u8 reg, u8 vl, u8 vh) |
126 | { |
127 | u8 *buffer; |
128 | int ret; |
129 | |
130 | /* 13: maximum length of message */ |
131 | buffer = kmalloc(size: 13, GFP_KERNEL); |
132 | if (!buffer) |
133 | return -ENOMEM; |
134 | |
135 | usb6fire_comm_init_buffer(buffer, id: 0x00, request, reg, vl, vh); |
136 | ret = usb6fire_comm_send_buffer(buffer, dev: rt->chip->dev); |
137 | |
138 | kfree(objp: buffer); |
139 | return ret; |
140 | } |
141 | |
142 | int usb6fire_comm_init(struct sfire_chip *chip) |
143 | { |
144 | struct comm_runtime *rt = kzalloc(size: sizeof(struct comm_runtime), |
145 | GFP_KERNEL); |
146 | struct urb *urb; |
147 | int ret; |
148 | |
149 | if (!rt) |
150 | return -ENOMEM; |
151 | |
152 | rt->receiver_buffer = kzalloc(size: COMM_RECEIVER_BUFSIZE, GFP_KERNEL); |
153 | if (!rt->receiver_buffer) { |
154 | kfree(objp: rt); |
155 | return -ENOMEM; |
156 | } |
157 | |
158 | urb = &rt->receiver; |
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) { |
176 | kfree(objp: rt->receiver_buffer); |
177 | kfree(objp: rt); |
178 | dev_err(&chip->dev->dev, "cannot create comm data receiver." ); |
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(urb: &rt->receiver); |
191 | } |
192 | |
193 | void usb6fire_comm_destroy(struct sfire_chip *chip) |
194 | { |
195 | struct comm_runtime *rt = chip->comm; |
196 | |
197 | kfree(objp: rt->receiver_buffer); |
198 | kfree(objp: rt); |
199 | chip->comm = NULL; |
200 | } |
201 | |