blob: 2286e3bcd8e4a5abdaa7e061b5fbf3c73c04077f [file] [log] [blame]
Prashant Malanifd1e2002017-08-09 13:22:59 -07001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "midis/seq_handler.h"
6
7#include <map>
Ben Chand496e612017-09-29 00:20:50 -07008#include <memory>
Prashant Malanifd1e2002017-08-09 13:22:59 -07009#include <string>
10#include <utility>
11
12#include <base/bind.h>
Prashant Malanifd1e2002017-08-09 13:22:59 -070013#include <poll.h>
14
15namespace {
16
17const unsigned int kCreateInputPortCaps =
18 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT;
19const unsigned int kCreateOutputPortCaps =
20 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT;
21const unsigned int kCreatePortType =
22 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
23const char kSndSeqName[] = "hw";
24
25} // namespace
26
27namespace midis {
28
29SeqHandler::SeqHandler(AddDeviceCallback add_device_cb,
30 RemoveDeviceCallback remove_device_cb,
31 HandleReceiveDataCallback handle_rx_data_cb,
32 IsDevicePresentCallback is_device_present_cb,
33 IsPortPresentCallback is_port_present_cb)
34 : add_device_cb_(add_device_cb),
35 remove_device_cb_(remove_device_cb),
36 handle_rx_data_cb_(handle_rx_data_cb),
37 is_device_present_cb_(is_device_present_cb),
38 is_port_present_cb_(is_port_present_cb),
39 weak_factory_(this) {}
40
41bool SeqHandler::InitSeq() {
42 // Create client handles.
43 snd_seq_t* tmp_seq = nullptr;
44 int err =
45 snd_seq_open(&tmp_seq, kSndSeqName, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
46 if (err != 0) {
47 LOG(ERROR) << "snd_seq_open fails: " << snd_strerror(err);
48 return false;
49 }
50 ScopedSeqPtr in_client(tmp_seq);
51 tmp_seq = nullptr;
52 in_client_id_ = snd_seq_client_id(in_client.get());
53
54 err = snd_seq_open(&tmp_seq, kSndSeqName, SND_SEQ_OPEN_OUTPUT, 0);
55 if (err != 0) {
56 LOG(ERROR) << "snd_seq_open fails: " << snd_strerror(err);
57 return false;
58 }
59
60 ScopedSeqPtr out_client(tmp_seq);
61 tmp_seq = nullptr;
62 out_client_id_ = snd_seq_client_id(out_client.get());
63
64 // Name the clients.
65 err = snd_seq_set_client_name(in_client.get(), "midis (input)");
66 if (err != 0) {
67 LOG(ERROR) << "snd_seq_set_client_name fails: " << snd_strerror(err);
68 return false;
69 }
70 err = snd_seq_set_client_name(out_client.get(), "midis (output)");
71 if (err != 0) {
72 LOG(ERROR) << "snd_seq_set_client_name fails: " << snd_strerror(err);
73 return false;
74 }
75
76 // Create input port.
77 in_port_id_ = snd_seq_create_simple_port(
78 in_client.get(), NULL, kCreateInputPortCaps, kCreatePortType);
79 if (in_port_id_ < 0) {
80 LOG(ERROR) << "snd_seq_create_simple_port fails: "
81 << snd_strerror(in_port_id_);
82 return false;
83 }
84
85 // Subscribe to the announce port.
86 snd_seq_port_subscribe_t* subs;
87 snd_seq_port_subscribe_alloca(&subs);
88 snd_seq_addr_t announce_sender;
89 snd_seq_addr_t announce_dest;
90 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
91 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
92 announce_dest.client = in_client_id_;
93 announce_dest.port = in_port_id_;
94 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
95 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
96 err = snd_seq_subscribe_port(in_client.get(), subs);
97 if (err != 0) {
98 LOG(ERROR) << "snd_seq_subscribe_port on the announce port fails: "
99 << snd_strerror(err);
100 return false;
101 }
102
103 // Initialize decoder.
104 snd_midi_event_t* tmp_decoder = nullptr;
105 snd_midi_event_new(0, &tmp_decoder);
106 ScopedMidiEventPtr decoder(tmp_decoder);
107 tmp_decoder = nullptr;
108 snd_midi_event_no_status(decoder.get(), 1);
109
110 in_client_.reset(in_client.release());
111 out_client_.reset(out_client.release());
112 decoder_.reset(decoder.release());
113
114 // Obtain the poll file descriptor to watch.
Ben Chand496e612017-09-29 00:20:50 -0700115 pfd_ = std::make_unique<pollfd>();
Prashant Malanifd1e2002017-08-09 13:22:59 -0700116 snd_seq_poll_descriptors(in_client_.get(), pfd_.get(), 1, POLLIN);
117
118 taskid_ = brillo::MessageLoop::current()->WatchFileDescriptor(
119 FROM_HERE,
120 pfd_.get()->fd,
121 brillo::MessageLoop::kWatchRead,
122 true,
123 base::Bind(&SeqHandler::ProcessAlsaClientFd, weak_factory_.GetWeakPtr()));
124
125 if (taskid_ == brillo::MessageLoop::kTaskIdNull) {
126 in_client_.reset();
127 out_client_.reset();
128 decoder_.reset();
129 pfd_.reset();
130 return false;
131 }
132
133 return true;
134}
135
136void SeqHandler::ProcessAlsaClientFd() {
137 int remaining;
138 do {
139 snd_seq_event_t* event;
140 int err = snd_seq_event_input(in_client_.get(), &event);
141 remaining = snd_seq_event_input_pending(in_client_.get(), 0);
142
143 if (err == -ENOSPC) {
144 // Handle out of space error.
145 LOG(ERROR) << "snd_seq_event_input detected buffer overrun";
146 // We've lost events: check another way to see if we need to shut
147 // down.
148 } else if (err == -EAGAIN) {
149 // We've read all the data.
150 } else if (err < 0) {
151 // Handle other errors.
152 LOG(ERROR) << "snd_seq_event_input fails: " << snd_strerror(err);
153 // TODO(pmalani): Stop the message loop here then.
154 } else if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
155 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
156 // Handle announce events.
157 switch (event->type) {
158 case SND_SEQ_EVENT_PORT_START:
159 // Don't use SND_SEQ_EVENT_CLIENT_START because the
160 // client name may not be set by the time we query
161 // it. It should be set by the time ports are made.
162 AddSeqDevice(event->data.addr.client);
163 AddSeqPort(event->data.addr.client, event->data.addr.port);
164 break;
165 case SND_SEQ_EVENT_CLIENT_EXIT:
166 // Check for disconnection of our "out" client. This means "shut
167 // down".
168 if (event->data.addr.client == out_client_id_) {
169 // TODO(pmalani): Stop the message loop here then.
170 remaining = 0;
171 } else {
172 RemoveSeqDevice(event->data.addr.client);
173 }
174 break;
175 case SND_SEQ_EVENT_PORT_EXIT:
176 RemoveSeqPort(event->data.addr.client, event->data.addr.port);
177 break;
178 }
179 } else {
180 // Normal operation.
181 ProcessMidiEvent(event);
182 }
183 } while (remaining > 0);
184}
185
186void SeqHandler::AddSeqDevice(uint32_t device_id) {
187 if (is_device_present_cb_.Run(0 /* FIXME: Remove card number */, device_id)) {
188 LOG(INFO) << "Device: " << device_id << " already exists.";
189 return;
190 }
191
192 // Check that the device isn't our own in/our client.
193 if (device_id == in_client_id_ || device_id == out_client_id_) {
194 return;
195 }
196
197 snd_seq_client_info_t* client_info;
198 snd_seq_client_info_alloca(&client_info);
199 int err =
200 snd_seq_get_any_client_info(in_client_.get(), device_id, client_info);
201 if (err != 0) {
202 LOG(ERROR) << "Failed to get client info.";
203 return;
204 }
205
206 std::string name(snd_seq_client_info_get_name(client_info));
207 uint32_t num_subdevices = snd_seq_client_info_get_num_ports(client_info);
208
209 // Store the list of MIDI ports and corresponding capabilities in a map.
210 std::map<uint32_t, unsigned int> port_caps;
211 snd_seq_port_info_t* port_info;
212 snd_seq_port_info_alloca(&port_info);
213 snd_seq_port_info_set_client(port_info, device_id);
214 snd_seq_port_info_set_port(port_info, -1);
215 while (!snd_seq_query_next_port(in_client_.get(), port_info)) {
216 if (!(snd_seq_port_info_get_type(port_info) &
217 SND_SEQ_PORT_TYPE_MIDI_GENERIC)) {
218 LOG(INFO) << "Skipping non-MIDI port.";
219 continue;
220 }
221 port_caps.emplace(snd_seq_port_info_get_port(port_info),
222 snd_seq_port_info_get_capability(port_info));
223 }
224
225 // FIXME(pmalani): Remove card/sys_num entirely (or retrieve it from snd_seq,
226 // and fill it in correctly.
Ben Chand496e612017-09-29 00:20:50 -0700227 auto dev = std::make_unique<Device>(
Prashant Malanifd1e2002017-08-09 13:22:59 -0700228 name,
229 std::string(),
230 0 /* card number; FIXME remove card number */,
231 device_id,
232 num_subdevices,
233 0 /* device flags FIXME */,
234 base::Bind(&SeqHandler::SubscribeInPort, base::Unretained(this)),
235 base::Bind(&SeqHandler::SubscribeOutPort, base::Unretained(this)),
236 base::Bind(&SeqHandler::UnsubscribeInPort, weak_factory_.GetWeakPtr()),
237 base::Bind(&SeqHandler::UnsubscribeOutPort, weak_factory_.GetWeakPtr()),
238 base::Bind(&SeqHandler::SendMidiData, weak_factory_.GetWeakPtr()),
239 std::move(port_caps));
240 add_device_cb_.Run(std::move(dev));
241}
242
243void SeqHandler::AddSeqPort(uint32_t device_id, uint32_t port_id) {
244 if (!is_port_present_cb_.Run(0, device_id, port_id)) {
245 LOG(WARNING) << "Received port start event for new port: " << port_id
246 << " on device: " << device_id << "; ignoring";
247 }
248}
249
250void SeqHandler::RemoveSeqDevice(uint32_t device_id) {
251 remove_device_cb_.Run(0 /* FIXME remove card number */, device_id);
252}
253
254void SeqHandler::RemoveSeqPort(uint32_t device_id, uint32_t port_id) {
255 if (!is_port_present_cb_.Run(0, device_id, port_id)) {
256 LOG(WARNING) << "Received port start event for new port: " << port_id
257 << " on device: " << device_id << "; ignoring";
258 }
259}
260
261bool SeqHandler::SubscribeInPort(uint32_t device_id, uint32_t port_id) {
262 snd_seq_port_subscribe_t* subs;
263 snd_seq_port_subscribe_alloca(&subs);
264 snd_seq_addr_t sender;
265 sender.client = device_id;
266 sender.port = port_id;
267 snd_seq_port_subscribe_set_sender(subs, &sender);
268
269 snd_seq_addr_t dest;
270 dest.client = in_client_id_;
271 dest.port = in_port_id_;
272 snd_seq_port_subscribe_set_dest(subs, &dest);
273
274 int err = snd_seq_subscribe_port(in_client_.get(), subs);
275 if (err != 0) {
276 LOG(ERROR) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
277 return false;
278 }
279
280 return true;
281}
282
283int SeqHandler::SubscribeOutPort(uint32_t device_id, uint32_t port_id) {
284 int out_port;
285 out_port = snd_seq_create_simple_port(
286 out_client_.get(), NULL, kCreateOutputPortCaps, kCreatePortType);
287 if (out_port < 0) {
288 LOG(INFO) << "snd_seq_creat_simple_port (output) failed: "
289 << snd_strerror(out_port);
290 return -1;
291 }
292
293 snd_seq_port_subscribe_t* subs;
294 snd_seq_port_subscribe_alloca(&subs);
295 snd_seq_addr_t sender;
296 sender.client = out_client_id_;
297 sender.port = out_port;
298 snd_seq_port_subscribe_set_sender(subs, &sender);
299
300 snd_seq_addr_t dest;
301 dest.client = device_id;
302 dest.port = port_id;
303 snd_seq_port_subscribe_set_dest(subs, &dest);
304
305 int err = snd_seq_subscribe_port(out_client_.get(), subs);
306 if (err != 0) {
307 snd_seq_delete_simple_port(out_client_.get(), out_port);
308 LOG(ERROR) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
309 return -1;
310 }
311
312 return out_port;
313}
314
315void SeqHandler::UnsubscribeInPort(uint32_t device_id, uint32_t port_id) {
316 snd_seq_port_subscribe_t* subs;
317 snd_seq_port_subscribe_alloca(&subs);
318 snd_seq_addr_t sender;
319 sender.client = device_id;
320 sender.port = port_id;
321 snd_seq_port_subscribe_set_sender(subs, &sender);
322 snd_seq_addr_t dest;
323 dest.client = in_client_id_;
324 dest.port = in_port_id_;
325 snd_seq_port_subscribe_set_dest(subs, &dest);
326
327 int err = snd_seq_unsubscribe_port(in_client_.get(), subs);
328 if (err != 0) {
329 LOG(WARNING) << "snd_seq_unsubscribe_port fails: " << snd_strerror(err);
330 return;
331 }
332}
333
334void SeqHandler::UnsubscribeOutPort(int out_port_id) {
335 snd_seq_delete_simple_port(out_client_.get(), out_port_id);
336}
337
338void SeqHandler::SendMidiData(int out_port_id,
339 const uint8_t* buffer,
340 size_t buf_len) {
341 snd_midi_event_t* encoder;
342 snd_midi_event_new(buf_len, &encoder);
343 for (int i = 0; i < buf_len; i++) {
344 snd_seq_event_t event;
345 int result = snd_midi_event_encode_byte(encoder, buffer[i], &event);
346 if (result == 1) {
347 // Send the message.
348 snd_seq_ev_set_source(&event, out_port_id);
349 snd_seq_ev_set_subs(&event);
350 snd_seq_ev_set_direct(&event);
351 snd_seq_event_output_direct(out_client_.get(), &event);
352 }
353 }
354 snd_midi_event_free(encoder);
355}
356
357void SeqHandler::ProcessMidiEvent(snd_seq_event_t* event) {
358 uint32_t device_id = event->source.client;
359 uint32_t subdevice_num = event->source.port;
360
361 if (event->type == SND_SEQ_EVENT_SYSEX) {
362 // SysEX, so pass it through without decoding.
363 handle_rx_data_cb_.Run(0,
364 device_id,
365 subdevice_num,
366 static_cast<char*>(event->data.ext.ptr),
367 event->data.ext.len);
368 } else {
369 // Normal message, so decode and send.
370 unsigned char buf[12];
371 int64_t count =
372 snd_midi_event_decode(decoder_.get(), buf, sizeof(buf), event);
373 if (count <= 0) {
374 if (count != -ENOENT) {
375 LOG(ERROR) << "snd_midi_event_decoder failed: " << snd_strerror(count);
376 }
377 } else {
378 handle_rx_data_cb_.Run(
379 0, device_id, subdevice_num, reinterpret_cast<char*>(buf), count);
380 }
381 }
382}
383
384} // namespace midis