blob: a3c37b8b6ecee2e2e8c33ebcb79e886cf3e482c4 [file] [log] [blame]
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +00001// Copyright 2014 The Chromium 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
dnicoara@chromium.org9f2a6f02014-01-03 21:25:00 +00005#include "media/midi/midi_manager_alsa.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +00006
mostynbbd693172015-11-17 19:06:54 -08007#include <errno.h>
agoode975043d2015-05-11 00:46:17 -07008#include <poll.h>
avi793390d2015-12-22 22:22:36 -08009#include <stddef.h>
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000010#include <stdlib.h>
danakj75afea02016-04-25 20:36:04 -070011
yhirano@chromium.orgcfa642c2014-05-01 08:54:41 +000012#include <algorithm>
13#include <string>
limasdfe59d0392015-11-19 20:28:57 -080014#include <utility>
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000015
16#include "base/bind.h"
agoodef212b2a2015-03-19 12:53:23 -070017#include "base/json/json_string_value_serializer.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000018#include "base/logging.h"
avi793390d2015-12-22 22:22:36 -080019#include "base/macros.h"
danakj75afea02016-04-25 20:36:04 -070020#include "base/memory/ptr_util.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000021#include "base/message_loop/message_loop.h"
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000022#include "base/posix/eintr_wrapper.h"
brettwf7f870f2015-06-09 11:05:24 -070023#include "base/posix/safe_strerror.h"
agoodef212b2a2015-03-19 12:53:23 -070024#include "base/strings/string_number_conversions.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000025#include "base/strings/stringprintf.h"
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000026#include "base/time/time.h"
agoodef212b2a2015-03-19 12:53:23 -070027#include "crypto/sha2.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000028#include "media/midi/midi_port_info.h"
29
30namespace media {
toyoshime147c5e2015-05-07 21:58:31 -070031namespace midi {
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000032
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000033namespace {
34
agoode@chromium.org25227512014-06-08 05:12:05 +000035// Per-output buffer. This can be smaller, but then large sysex messages
36// will be (harmlessly) split across multiple seq events. This should
37// not have any real practical effect, except perhaps to slightly reorder
38// realtime messages with respect to sysex.
39const size_t kSendBufferSize = 256;
40
agoodeb09423b2015-05-11 11:39:57 -070041// Minimum client id for which we will have ALSA card devices for. When we
42// are searching for card devices (used to get the path, id, and manufacturer),
43// we don't want to get confused by kernel clients that do not have a card.
44// See seq_clientmgr.c in the ALSA code for this.
45// TODO(agoode): Add proper client -> card export from the kernel to avoid
46// hardcoding.
47const int kMinimumClientIdForCards = 16;
48
agoodef212b2a2015-03-19 12:53:23 -070049// ALSA constants.
50const char kAlsaHw[] = "hw";
51
agoode975043d2015-05-11 00:46:17 -070052// udev constants.
53const char kUdev[] = "udev";
54const char kUdevSubsystemSound[] = "sound";
55const char kUdevPropertySoundInitialized[] = "SOUND_INITIALIZED";
56const char kUdevActionChange[] = "change";
57const char kUdevActionRemove[] = "remove";
58
agoodeb09423b2015-05-11 11:39:57 -070059const char kUdevIdVendor[] = "ID_VENDOR";
60const char kUdevIdVendorEnc[] = "ID_VENDOR_ENC";
61const char kUdevIdVendorFromDatabase[] = "ID_VENDOR_FROM_DATABASE";
62const char kUdevIdVendorId[] = "ID_VENDOR_ID";
63const char kUdevIdModelId[] = "ID_MODEL_ID";
64const char kUdevIdBus[] = "ID_BUS";
65const char kUdevIdPath[] = "ID_PATH";
66const char kUdevIdUsbInterfaceNum[] = "ID_USB_INTERFACE_NUM";
67const char kUdevIdSerialShort[] = "ID_SERIAL_SHORT";
68
69const char kSysattrVendorName[] = "vendor_name";
70const char kSysattrVendor[] = "vendor";
71const char kSysattrModel[] = "model";
72const char kSysattrGuid[] = "guid";
73
74const char kCardSyspath[] = "/card";
75
agoode@chromium.org25227512014-06-08 05:12:05 +000076// Constants for the capabilities we search for in inputs and outputs.
77// See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
78const unsigned int kRequiredInputPortCaps =
79 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
80const unsigned int kRequiredOutputPortCaps =
81 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
82
agoode99d63292015-04-13 08:39:25 -070083const unsigned int kCreateOutputPortCaps =
84 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT;
85const unsigned int kCreateInputPortCaps =
86 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT;
87const unsigned int kCreatePortType =
88 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
agoode@chromium.org25227512014-06-08 05:12:05 +000089
agoode99d63292015-04-13 08:39:25 -070090int AddrToInt(int client, int port) {
91 return (client << 8) | port;
agoodef212b2a2015-03-19 12:53:23 -070092}
agoodef212b2a2015-03-19 12:53:23 -070093
agoodeb09423b2015-05-11 11:39:57 -070094// Returns true if this client has an ALSA card associated with it.
95bool IsCardClient(snd_seq_client_type_t type, int client_id) {
96 return (type == SND_SEQ_KERNEL_CLIENT) &&
97 (client_id >= kMinimumClientIdForCards);
98}
99
100// TODO(agoode): Move this to device/udev_linux.
101const std::string UdevDeviceGetPropertyOrSysattr(
102 struct udev_device* udev_device,
103 const char* property_key,
104 const char* sysattr_key) {
105 // First try the property.
106 std::string value =
107 device::UdevDeviceGetPropertyValue(udev_device, property_key);
108
109 // If no property, look for sysattrs and walk up the parent devices too.
110 while (value.empty() && udev_device) {
111 value = device::UdevDeviceGetSysattrValue(udev_device, sysattr_key);
112 udev_device = device::udev_device_get_parent(udev_device);
113 }
114 return value;
115}
116
117int GetCardNumber(udev_device* dev) {
118 const char* syspath = device::udev_device_get_syspath(dev);
119 if (!syspath)
120 return -1;
121
122 std::string syspath_str(syspath);
123 size_t i = syspath_str.rfind(kCardSyspath);
124 if (i == std::string::npos)
125 return -1;
126
127 int number;
128 if (!base::StringToInt(syspath_str.substr(i + strlen(kCardSyspath)), &number))
129 return -1;
130 return number;
131}
132
agooded87fc0f2015-05-21 08:29:31 -0700133std::string GetVendor(udev_device* dev) {
134 // Try to get the vendor string. Sometimes it is encoded.
135 std::string vendor = device::UdevDecodeString(
136 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorEnc));
137 // Sometimes it is not encoded.
138 if (vendor.empty())
139 vendor =
140 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendor, kSysattrVendorName);
141 return vendor;
142}
143
agoode8caab0b2015-03-23 18:48:02 -0700144void SetStringIfNonEmpty(base::DictionaryValue* value,
145 const std::string& path,
146 const std::string& in_value) {
147 if (!in_value.empty())
148 value->SetString(path, in_value);
149}
150
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000151} // namespace
152
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000153MidiManagerAlsa::MidiManagerAlsa()
agoodeb2ead822016-03-11 12:14:35 -0800154 : event_thread_("MidiEventThread"), send_thread_("MidiSendThread") {}
155
156MidiManagerAlsa::~MidiManagerAlsa() {
157 // Take lock to ensure that the members initialized on the IO thread
158 // are not destructed here.
159 base::AutoLock lock(lazy_init_member_lock_);
160
toyoshim131beb52016-07-25 00:42:41 -0700161 // Extra CHECK to verify all members are already reset.
162 CHECK(!initialization_thread_checker_);
163 CHECK(!in_client_);
164 CHECK(!out_client_);
165 CHECK(!decoder_);
166 CHECK(!udev_);
167 CHECK(!udev_monitor_);
168
169 CHECK(!send_thread_.IsRunning());
170 CHECK(!event_thread_.IsRunning());
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000171}
172
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000173void MidiManagerAlsa::StartInitialization() {
agoodeb2ead822016-03-11 12:14:35 -0800174 base::AutoLock lock(lazy_init_member_lock_);
agoode5a1aa112015-06-21 20:51:00 -0700175
agoodeb2ead822016-03-11 12:14:35 -0800176 initialization_thread_checker_.reset(new base::ThreadChecker());
177
178 // Create client handles.
179 snd_seq_t* tmp_seq = nullptr;
180 int err =
181 snd_seq_open(&tmp_seq, kAlsaHw, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
agoode@chromium.org25227512014-06-08 05:12:05 +0000182 if (err != 0) {
183 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700184 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000185 }
agoodeb2ead822016-03-11 12:14:35 -0800186 ScopedSndSeqPtr in_client(tmp_seq);
187 tmp_seq = nullptr;
188 in_client_id_ = snd_seq_client_id(in_client.get());
189
190 err = snd_seq_open(&tmp_seq, kAlsaHw, SND_SEQ_OPEN_OUTPUT, 0);
191 if (err != 0) {
192 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
193 return CompleteInitialization(Result::INITIALIZATION_ERROR);
194 }
195 ScopedSndSeqPtr out_client(tmp_seq);
196 tmp_seq = nullptr;
197 out_client_id_ = snd_seq_client_id(out_client.get());
agoode@chromium.org25227512014-06-08 05:12:05 +0000198
199 // Name the clients.
agoodeb2ead822016-03-11 12:14:35 -0800200 err = snd_seq_set_client_name(in_client.get(), "Chrome (input)");
agoode@chromium.org25227512014-06-08 05:12:05 +0000201 if (err != 0) {
202 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700203 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000204 }
agoodeb2ead822016-03-11 12:14:35 -0800205 err = snd_seq_set_client_name(out_client.get(), "Chrome (output)");
agoode@chromium.org25227512014-06-08 05:12:05 +0000206 if (err != 0) {
207 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700208 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000209 }
210
211 // Create input port.
agoode99d63292015-04-13 08:39:25 -0700212 in_port_id_ = snd_seq_create_simple_port(
agoodeb2ead822016-03-11 12:14:35 -0800213 in_client.get(), NULL, kCreateInputPortCaps, kCreatePortType);
agoode99d63292015-04-13 08:39:25 -0700214 if (in_port_id_ < 0) {
215 VLOG(1) << "snd_seq_create_simple_port fails: "
216 << snd_strerror(in_port_id_);
toyoshimf1b88962015-07-09 14:14:51 -0700217 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000218 }
219
220 // Subscribe to the announce port.
221 snd_seq_port_subscribe_t* subs;
222 snd_seq_port_subscribe_alloca(&subs);
223 snd_seq_addr_t announce_sender;
224 snd_seq_addr_t announce_dest;
225 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
226 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
agoode99d63292015-04-13 08:39:25 -0700227 announce_dest.client = in_client_id_;
228 announce_dest.port = in_port_id_;
agoode@chromium.org25227512014-06-08 05:12:05 +0000229 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
230 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
agoodeb2ead822016-03-11 12:14:35 -0800231 err = snd_seq_subscribe_port(in_client.get(), subs);
agoode@chromium.org25227512014-06-08 05:12:05 +0000232 if (err != 0) {
233 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
234 << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700235 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000236 }
237
agoodeb2ead822016-03-11 12:14:35 -0800238 // Initialize decoder.
239 snd_midi_event_t* tmp_decoder = nullptr;
240 snd_midi_event_new(0, &tmp_decoder);
241 ScopedSndMidiEventPtr decoder(tmp_decoder);
242 tmp_decoder = nullptr;
243 snd_midi_event_no_status(decoder.get(), 1);
agoode@chromium.org25227512014-06-08 05:12:05 +0000244
agoodeb2ead822016-03-11 12:14:35 -0800245 // Initialize udev and monitor.
246 device::ScopedUdevPtr udev(device::udev_new());
247 device::ScopedUdevMonitorPtr udev_monitor(
248 device::udev_monitor_new_from_netlink(udev.get(), kUdev));
249 if (!udev_monitor.get()) {
agoode975043d2015-05-11 00:46:17 -0700250 VLOG(1) << "udev_monitor_new_from_netlink fails";
toyoshimf1b88962015-07-09 14:14:51 -0700251 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700252 }
253 err = device::udev_monitor_filter_add_match_subsystem_devtype(
agoodeb2ead822016-03-11 12:14:35 -0800254 udev_monitor.get(), kUdevSubsystemSound, nullptr);
agoode975043d2015-05-11 00:46:17 -0700255 if (err != 0) {
256 VLOG(1) << "udev_monitor_add_match_subsystem fails: "
brettwf7f870f2015-06-09 11:05:24 -0700257 << base::safe_strerror(-err);
toyoshimf1b88962015-07-09 14:14:51 -0700258 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700259 }
agoodeb2ead822016-03-11 12:14:35 -0800260 err = device::udev_monitor_enable_receiving(udev_monitor.get());
agoode975043d2015-05-11 00:46:17 -0700261 if (err != 0) {
brettwf7f870f2015-06-09 11:05:24 -0700262 VLOG(1) << "udev_monitor_enable_receiving fails: "
263 << base::safe_strerror(-err);
toyoshimf1b88962015-07-09 14:14:51 -0700264 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700265 }
266
agoodeb2ead822016-03-11 12:14:35 -0800267 // Success! Now, initialize members from the temporaries. Do not
268 // initialize these earlier, since they need to be destroyed by the
269 // thread that calls Finalize(), not the destructor thread (and we
270 // check this in the destructor).
271 in_client_.reset(in_client.release());
272 out_client_.reset(out_client.release());
273 decoder_.reset(decoder.release());
274 udev_.reset(udev.release());
agoode740f5222016-07-06 23:46:32 -0700275 udev_monitor_.reset(udev_monitor.release());
agoodeb2ead822016-03-11 12:14:35 -0800276
277 // Generate hotplug events for existing ports.
278 // TODO(agoode): Check the return value for failure.
279 EnumerateAlsaPorts();
280
281 // Generate hotplug events for existing udev devices. This must be done
282 // after udev_monitor_enable_receiving() is called. See the algorithm
283 // at http://www.signal11.us/oss/udev/.
agoode975043d2015-05-11 00:46:17 -0700284 EnumerateUdevCards();
285
agoodeb2ead822016-03-11 12:14:35 -0800286 // Start processing events. Don't do this before enumeration of both
287 // ALSA and udev.
agoode@chromium.org25227512014-06-08 05:12:05 +0000288 event_thread_.Start();
289 event_thread_.message_loop()->PostTask(
290 FROM_HERE,
agoodebd4be9b2015-03-16 19:17:25 -0700291 base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this)));
agoodeb2ead822016-03-11 12:14:35 -0800292 send_thread_.Start();
agoode@chromium.org25227512014-06-08 05:12:05 +0000293
toyoshimf1b88962015-07-09 14:14:51 -0700294 CompleteInitialization(Result::OK);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000295}
296
toyoshim8e7d6e02015-10-06 08:47:17 -0700297void MidiManagerAlsa::Finalize() {
agoodeb2ead822016-03-11 12:14:35 -0800298 base::AutoLock lock(lazy_init_member_lock_);
299 DCHECK(initialization_thread_checker_->CalledOnValidThread());
300
toyoshim8e7d6e02015-10-06 08:47:17 -0700301 // Tell the event thread it will soon be time to shut down. This gives
302 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
303 // message is lost.
304 {
305 base::AutoLock lock(shutdown_lock_);
306 event_thread_shutdown_ = true;
307 }
308
309 // Stop the send thread.
310 send_thread_.Stop();
311
312 // Close the out client. This will trigger the event thread to stop,
313 // because of SND_SEQ_EVENT_CLIENT_EXIT.
agoodeb2ead822016-03-11 12:14:35 -0800314 out_client_.reset();
toyoshim8e7d6e02015-10-06 08:47:17 -0700315
316 // Wait for the event thread to stop.
317 event_thread_.Stop();
agoodeb2ead822016-03-11 12:14:35 -0800318
319 // Destruct the other stuff we initialized in StartInitialization().
320 udev_monitor_.reset();
321 udev_.reset();
322 decoder_.reset();
323 in_client_.reset();
324 initialization_thread_checker_.reset();
toyoshim8e7d6e02015-10-06 08:47:17 -0700325}
326
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000327void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500328 uint32_t port_index,
329 const std::vector<uint8_t>& data,
dnicoara@chromium.org9f2a6f02014-01-03 21:25:00 +0000330 double timestamp) {
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000331 base::TimeDelta delay;
332 if (timestamp != 0.0) {
333 base::TimeTicks time_to_send =
334 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
agoodebd4be9b2015-03-16 19:17:25 -0700335 timestamp * base::Time::kMicrosecondsPerSecond);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000336 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
337 }
338
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000339 send_thread_.message_loop()->PostDelayedTask(
agoodebd4be9b2015-03-16 19:17:25 -0700340 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData,
341 base::Unretained(this), port_index, data),
342 delay);
agoode@chromium.org25227512014-06-08 05:12:05 +0000343
344 // Acknowledge send.
345 send_thread_.message_loop()->PostTask(
agoodead116b22015-12-07 00:00:35 -0800346 FROM_HERE, base::Bind(&MidiManagerAlsa::AccumulateMidiBytesSent,
347 base::Unretained(this), client, data.size()));
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000348}
349
agooded87fc0f2015-05-21 08:29:31 -0700350MidiManagerAlsa::MidiPort::Id::Id() = default;
351
352MidiManagerAlsa::MidiPort::Id::Id(const std::string& bus,
353 const std::string& vendor_id,
354 const std::string& model_id,
355 const std::string& usb_interface_num,
356 const std::string& serial)
357 : bus_(bus),
358 vendor_id_(vendor_id),
359 model_id_(model_id),
360 usb_interface_num_(usb_interface_num),
361 serial_(serial) {
362}
363
364MidiManagerAlsa::MidiPort::Id::Id(const Id&) = default;
365
366MidiManagerAlsa::MidiPort::Id::~Id() = default;
367
368bool MidiManagerAlsa::MidiPort::Id::operator==(const Id& rhs) const {
369 return (bus_ == rhs.bus_) && (vendor_id_ == rhs.vendor_id_) &&
370 (model_id_ == rhs.model_id_) &&
371 (usb_interface_num_ == rhs.usb_interface_num_) &&
372 (serial_ == rhs.serial_);
373}
374
375bool MidiManagerAlsa::MidiPort::Id::empty() const {
376 return bus_.empty() && vendor_id_.empty() && model_id_.empty() &&
377 usb_interface_num_.empty() && serial_.empty();
378}
379
agoode99d63292015-04-13 08:39:25 -0700380MidiManagerAlsa::MidiPort::MidiPort(const std::string& path,
agooded87fc0f2015-05-21 08:29:31 -0700381 const Id& id,
agoode99d63292015-04-13 08:39:25 -0700382 int client_id,
383 int port_id,
384 int midi_device,
385 const std::string& client_name,
386 const std::string& port_name,
387 const std::string& manufacturer,
388 const std::string& version,
389 Type type)
390 : id_(id),
391 midi_device_(midi_device),
392 type_(type),
393 path_(path),
394 client_id_(client_id),
395 port_id_(port_id),
396 client_name_(client_name),
397 port_name_(port_name),
398 manufacturer_(manufacturer),
agoode5a1aa112015-06-21 20:51:00 -0700399 version_(version) {
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000400}
401
agoodeb0582872015-05-20 05:22:24 -0700402MidiManagerAlsa::MidiPort::~MidiPort() = default;
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000403
agoode99d63292015-04-13 08:39:25 -0700404// Note: keep synchronized with the MidiPort::Match* methods.
danakj75afea02016-04-25 20:36:04 -0700405std::unique_ptr<base::Value> MidiManagerAlsa::MidiPort::Value() const {
406 std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
agoode99d63292015-04-13 08:39:25 -0700407
408 std::string type;
409 switch (type_) {
410 case Type::kInput:
411 type = "input";
412 break;
agoode99d63292015-04-13 08:39:25 -0700413 case Type::kOutput:
414 type = "output";
415 break;
416 }
417 value->SetString("type", type);
418 SetStringIfNonEmpty(value.get(), "path", path_);
agoode99d63292015-04-13 08:39:25 -0700419 SetStringIfNonEmpty(value.get(), "clientName", client_name_);
420 SetStringIfNonEmpty(value.get(), "portName", port_name_);
421 value->SetInteger("clientId", client_id_);
422 value->SetInteger("portId", port_id_);
423 value->SetInteger("midiDevice", midi_device_);
424
agooded87fc0f2015-05-21 08:29:31 -0700425 // Flatten id fields.
426 SetStringIfNonEmpty(value.get(), "bus", id_.bus());
427 SetStringIfNonEmpty(value.get(), "vendorId", id_.vendor_id());
428 SetStringIfNonEmpty(value.get(), "modelId", id_.model_id());
429 SetStringIfNonEmpty(value.get(), "usbInterfaceNum", id_.usb_interface_num());
430 SetStringIfNonEmpty(value.get(), "serial", id_.serial());
431
dchengc2aeece2015-12-27 00:54:00 -0800432 return std::move(value);
agoodebd4be9b2015-03-16 19:17:25 -0700433}
agoode@chromium.org25227512014-06-08 05:12:05 +0000434
agoode99d63292015-04-13 08:39:25 -0700435std::string MidiManagerAlsa::MidiPort::JSONValue() const {
436 std::string json;
437 JSONStringValueSerializer serializer(&json);
438 serializer.Serialize(*Value().get());
439 return json;
agoodef212b2a2015-03-19 12:53:23 -0700440}
441
agoode99d63292015-04-13 08:39:25 -0700442// TODO(agoode): Do not use SHA256 here. Instead store a persistent
443// mapping and just use a UUID or other random string.
444// http://crbug.com/465320
445std::string MidiManagerAlsa::MidiPort::OpaqueKey() const {
Avi Drissman3528fd02015-12-18 20:11:31 -0500446 uint8_t hash[crypto::kSHA256Length];
agoode99d63292015-04-13 08:39:25 -0700447 crypto::SHA256HashString(JSONValue(), &hash, sizeof(hash));
448 return base::HexEncode(&hash, sizeof(hash));
agoodebd4be9b2015-03-16 19:17:25 -0700449}
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000450
agoode99d63292015-04-13 08:39:25 -0700451bool MidiManagerAlsa::MidiPort::MatchConnected(const MidiPort& query) const {
452 // Matches on:
453 // connected == true
454 // type
455 // path
456 // id
457 // client_id
458 // port_id
459 // midi_device
460 // client_name
461 // port_name
462 return connected() && (type() == query.type()) && (path() == query.path()) &&
463 (id() == query.id()) && (client_id() == query.client_id()) &&
464 (port_id() == query.port_id()) &&
465 (midi_device() == query.midi_device()) &&
466 (client_name() == query.client_name()) &&
467 (port_name() == query.port_name());
agoodebd4be9b2015-03-16 19:17:25 -0700468}
469
agoode99d63292015-04-13 08:39:25 -0700470bool MidiManagerAlsa::MidiPort::MatchCardPass1(const MidiPort& query) const {
471 // Matches on:
472 // connected == false
473 // type
474 // path
475 // id
476 // port_id
477 // midi_device
478 return MatchCardPass2(query) && (path() == query.path());
agoodebd4be9b2015-03-16 19:17:25 -0700479}
480
agoode99d63292015-04-13 08:39:25 -0700481bool MidiManagerAlsa::MidiPort::MatchCardPass2(const MidiPort& query) const {
482 // Matches on:
483 // connected == false
484 // type
485 // id
486 // port_id
487 // midi_device
488 return !connected() && (type() == query.type()) && (id() == query.id()) &&
489 (port_id() == query.port_id()) &&
490 (midi_device() == query.midi_device());
agoodef212b2a2015-03-19 12:53:23 -0700491}
492
agoode99d63292015-04-13 08:39:25 -0700493bool MidiManagerAlsa::MidiPort::MatchNoCardPass1(const MidiPort& query) const {
494 // Matches on:
495 // connected == false
496 // type
497 // path.empty(), for both this and query
498 // id.empty(), for both this and query
499 // client_id
500 // port_id
501 // client_name
502 // port_name
503 // midi_device == -1, for both this and query
504 return MatchNoCardPass2(query) && (client_id() == query.client_id());
agoodef212b2a2015-03-19 12:53:23 -0700505}
506
agoode99d63292015-04-13 08:39:25 -0700507bool MidiManagerAlsa::MidiPort::MatchNoCardPass2(const MidiPort& query) const {
508 // Matches on:
509 // connected == false
510 // type
511 // path.empty(), for both this and query
512 // id.empty(), for both this and query
513 // port_id
514 // client_name
515 // port_name
516 // midi_device == -1, for both this and query
517 return !connected() && (type() == query.type()) && path().empty() &&
518 query.path().empty() && id().empty() && query.id().empty() &&
519 (port_id() == query.port_id()) &&
520 (client_name() == query.client_name()) &&
521 (port_name() == query.port_name()) && (midi_device() == -1) &&
522 (query.midi_device() == -1);
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000523}
524
agoodeb0582872015-05-20 05:22:24 -0700525MidiManagerAlsa::MidiPortStateBase::~MidiPortStateBase() = default;
agoode99d63292015-04-13 08:39:25 -0700526
527MidiManagerAlsa::MidiPortStateBase::iterator
528MidiManagerAlsa::MidiPortStateBase::Find(
529 const MidiManagerAlsa::MidiPort& port) {
530 auto result = FindConnected(port);
531 if (result == end())
532 result = FindDisconnected(port);
533 return result;
534}
535
536MidiManagerAlsa::MidiPortStateBase::iterator
537MidiManagerAlsa::MidiPortStateBase::FindConnected(
538 const MidiManagerAlsa::MidiPort& port) {
539 // Exact match required for connected ports.
danakj75afea02016-04-25 20:36:04 -0700540 auto it = std::find_if(ports_.begin(), ports_.end(),
541 [&port](std::unique_ptr<MidiPort>& p) {
542 return p->MatchConnected(port);
543 });
agoode99d63292015-04-13 08:39:25 -0700544 return it;
545}
546
547MidiManagerAlsa::MidiPortStateBase::iterator
548MidiManagerAlsa::MidiPortStateBase::FindDisconnected(
549 const MidiManagerAlsa::MidiPort& port) {
550 // Always match on:
551 // type
552 // Possible things to match on:
553 // path
554 // id
555 // client_id
556 // port_id
557 // midi_device
558 // client_name
559 // port_name
560
561 if (!port.path().empty()) {
562 // If path is present, then we have a card-based client.
563
564 // Pass 1. Match on path, id, midi_device, port_id.
565 // This is the best possible match for hardware card-based clients.
566 // This will also match the empty id correctly for devices without an id.
danakj75afea02016-04-25 20:36:04 -0700567 auto it = std::find_if(ports_.begin(), ports_.end(),
568 [&port](std::unique_ptr<MidiPort>& p) {
569 return p->MatchCardPass1(port);
570 });
agoode99d63292015-04-13 08:39:25 -0700571 if (it != ports_.end())
572 return it;
573
574 if (!port.id().empty()) {
575 // Pass 2. Match on id, midi_device, port_id.
576 // This will give us a high-confidence match when a user moves a device to
577 // another USB/Firewire/Thunderbolt/etc port, but only works if the device
578 // has a hardware id.
danakj75afea02016-04-25 20:36:04 -0700579 it = std::find_if(ports_.begin(), ports_.end(),
580 [&port](std::unique_ptr<MidiPort>& p) {
581 return p->MatchCardPass2(port);
582 });
agoode99d63292015-04-13 08:39:25 -0700583 if (it != ports_.end())
584 return it;
585 }
586 } else {
587 // Else, we have a non-card-based client.
588 // Pass 1. Match on client_id, port_id, client_name, port_name.
589 // This will give us a reasonably good match.
danakj75afea02016-04-25 20:36:04 -0700590 auto it = std::find_if(ports_.begin(), ports_.end(),
591 [&port](std::unique_ptr<MidiPort>& p) {
592 return p->MatchNoCardPass1(port);
593 });
agoode99d63292015-04-13 08:39:25 -0700594 if (it != ports_.end())
595 return it;
596
597 // Pass 2. Match on port_id, client_name, port_name.
598 // This is weaker but similar to pass 2 in the hardware card-based clients
599 // match.
danakj75afea02016-04-25 20:36:04 -0700600 it = std::find_if(ports_.begin(), ports_.end(),
601 [&port](std::unique_ptr<MidiPort>& p) {
602 return p->MatchNoCardPass2(port);
603 });
agoode99d63292015-04-13 08:39:25 -0700604 if (it != ports_.end())
605 return it;
606 }
607
608 // No match.
609 return ports_.end();
610}
611
agoodeb0582872015-05-20 05:22:24 -0700612MidiManagerAlsa::MidiPortStateBase::MidiPortStateBase() = default;
agoode99d63292015-04-13 08:39:25 -0700613
agoodedf1b9ff2015-06-25 18:14:50 -0700614MidiManagerAlsa::MidiPortState::MidiPortState() = default;
agoode99d63292015-04-13 08:39:25 -0700615
danakj75afea02016-04-25 20:36:04 -0700616uint32_t MidiManagerAlsa::MidiPortState::push_back(
617 std::unique_ptr<MidiPort> port) {
agoode99d63292015-04-13 08:39:25 -0700618 // Add the web midi index.
Avi Drissman3528fd02015-12-18 20:11:31 -0500619 uint32_t web_port_index = 0;
agoode99d63292015-04-13 08:39:25 -0700620 switch (port->type()) {
621 case MidiPort::Type::kInput:
622 web_port_index = num_input_ports_++;
623 break;
624 case MidiPort::Type::kOutput:
625 web_port_index = num_output_ports_++;
626 break;
627 }
628 port->set_web_port_index(web_port_index);
dchengc2aeece2015-12-27 00:54:00 -0800629 MidiPortStateBase::push_back(std::move(port));
agoode99d63292015-04-13 08:39:25 -0700630 return web_port_index;
631}
632
agoodedf1b9ff2015-06-25 18:14:50 -0700633MidiManagerAlsa::AlsaSeqState::AlsaSeqState() = default;
agoode99d63292015-04-13 08:39:25 -0700634
agoodeb0582872015-05-20 05:22:24 -0700635MidiManagerAlsa::AlsaSeqState::~AlsaSeqState() = default;
agoode99d63292015-04-13 08:39:25 -0700636
637void MidiManagerAlsa::AlsaSeqState::ClientStart(int client_id,
638 const std::string& client_name,
639 snd_seq_client_type_t type) {
640 ClientExit(client_id);
limasdfe59d0392015-11-19 20:28:57 -0800641 clients_.insert(std::make_pair(
danakj75afea02016-04-25 20:36:04 -0700642 client_id, base::WrapUnique(new Client(client_name, type))));
agoodeb09423b2015-05-11 11:39:57 -0700643 if (IsCardClient(type, client_id))
644 ++card_client_count_;
agoode99d63292015-04-13 08:39:25 -0700645}
646
647bool MidiManagerAlsa::AlsaSeqState::ClientStarted(int client_id) {
648 return clients_.find(client_id) != clients_.end();
649}
650
651void MidiManagerAlsa::AlsaSeqState::ClientExit(int client_id) {
652 auto it = clients_.find(client_id);
653 if (it != clients_.end()) {
agoodeb09423b2015-05-11 11:39:57 -0700654 if (IsCardClient(it->second->type(), client_id))
655 --card_client_count_;
agoode99d63292015-04-13 08:39:25 -0700656 clients_.erase(it);
657 }
658}
659
660void MidiManagerAlsa::AlsaSeqState::PortStart(
661 int client_id,
662 int port_id,
663 const std::string& port_name,
664 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
665 bool midi) {
666 auto it = clients_.find(client_id);
667 if (it != clients_.end())
668 it->second->AddPort(port_id,
danakj75afea02016-04-25 20:36:04 -0700669 base::WrapUnique(new Port(port_name, direction, midi)));
agoode99d63292015-04-13 08:39:25 -0700670}
671
672void MidiManagerAlsa::AlsaSeqState::PortExit(int client_id, int port_id) {
673 auto it = clients_.find(client_id);
674 if (it != clients_.end())
675 it->second->RemovePort(port_id);
676}
677
678snd_seq_client_type_t MidiManagerAlsa::AlsaSeqState::ClientType(
679 int client_id) const {
680 auto it = clients_.find(client_id);
681 if (it == clients_.end())
682 return SND_SEQ_USER_CLIENT;
683 return it->second->type();
684}
685
danakj75afea02016-04-25 20:36:04 -0700686std::unique_ptr<MidiManagerAlsa::TemporaryMidiPortState>
agoodeb09423b2015-05-11 11:39:57 -0700687MidiManagerAlsa::AlsaSeqState::ToMidiPortState(const AlsaCardMap& alsa_cards) {
danakj75afea02016-04-25 20:36:04 -0700688 std::unique_ptr<MidiManagerAlsa::TemporaryMidiPortState> midi_ports(
agoode99d63292015-04-13 08:39:25 -0700689 new TemporaryMidiPortState);
agoodeb09423b2015-05-11 11:39:57 -0700690 auto card_it = alsa_cards.begin();
agoode99d63292015-04-13 08:39:25 -0700691
agoodeb09423b2015-05-11 11:39:57 -0700692 int card_midi_device = -1;
agoode99d63292015-04-13 08:39:25 -0700693 for (const auto& client_pair : clients_) {
694 int client_id = client_pair.first;
vmpstr0205abb2016-06-28 18:50:56 -0700695 auto* client = client_pair.second.get();
agoode99d63292015-04-13 08:39:25 -0700696
697 // Get client metadata.
698 const std::string client_name = client->name();
699 std::string manufacturer;
700 std::string driver;
701 std::string path;
agooded87fc0f2015-05-21 08:29:31 -0700702 MidiPort::Id id;
agoode99d63292015-04-13 08:39:25 -0700703 std::string card_name;
704 std::string card_longname;
705 int midi_device = -1;
706
agoodeb09423b2015-05-11 11:39:57 -0700707 if (IsCardClient(client->type(), client_id)) {
708 auto& card = card_it->second;
709 if (card_midi_device == -1)
710 card_midi_device = 0;
711
712 manufacturer = card->manufacturer();
agooded87fc0f2015-05-21 08:29:31 -0700713 path = card->path();
714 id = MidiPort::Id(card->bus(), card->vendor_id(), card->model_id(),
715 card->usb_interface_num(), card->serial());
716 card_name = card->name();
717 card_longname = card->longname();
agoodeb09423b2015-05-11 11:39:57 -0700718 midi_device = card_midi_device;
719
720 ++card_midi_device;
721 if (card_midi_device >= card->midi_device_count()) {
722 card_midi_device = -1;
723 ++card_it;
724 }
725 }
726
agoode99d63292015-04-13 08:39:25 -0700727 for (const auto& port_pair : *client) {
728 int port_id = port_pair.first;
729 const auto& port = port_pair.second;
730
731 if (port->midi()) {
732 std::string version;
733 if (!driver.empty()) {
734 version = driver + " / ";
735 }
736 version +=
737 base::StringPrintf("ALSA library version %d.%d.%d", SND_LIB_MAJOR,
738 SND_LIB_MINOR, SND_LIB_SUBMINOR);
739 PortDirection direction = port->direction();
740 if (direction == PortDirection::kInput ||
741 direction == PortDirection::kDuplex) {
danakj75afea02016-04-25 20:36:04 -0700742 midi_ports->push_back(base::WrapUnique(new MidiPort(
agoode99d63292015-04-13 08:39:25 -0700743 path, id, client_id, port_id, midi_device, client->name(),
744 port->name(), manufacturer, version, MidiPort::Type::kInput)));
745 }
746 if (direction == PortDirection::kOutput ||
747 direction == PortDirection::kDuplex) {
danakj75afea02016-04-25 20:36:04 -0700748 midi_ports->push_back(base::WrapUnique(new MidiPort(
agoode99d63292015-04-13 08:39:25 -0700749 path, id, client_id, port_id, midi_device, client->name(),
750 port->name(), manufacturer, version, MidiPort::Type::kOutput)));
751 }
752 }
753 }
754 }
755
dchengc2aeece2015-12-27 00:54:00 -0800756 return midi_ports;
agoode99d63292015-04-13 08:39:25 -0700757}
758
759MidiManagerAlsa::AlsaSeqState::Port::Port(
760 const std::string& name,
761 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
762 bool midi)
763 : name_(name), direction_(direction), midi_(midi) {
764}
765
agoodeb0582872015-05-20 05:22:24 -0700766MidiManagerAlsa::AlsaSeqState::Port::~Port() = default;
agoode99d63292015-04-13 08:39:25 -0700767
768MidiManagerAlsa::AlsaSeqState::Client::Client(const std::string& name,
769 snd_seq_client_type_t type)
mgiucad9af8452015-06-25 02:11:57 -0700770 : name_(name), type_(type) {
agoode99d63292015-04-13 08:39:25 -0700771}
772
agoodeb0582872015-05-20 05:22:24 -0700773MidiManagerAlsa::AlsaSeqState::Client::~Client() = default;
agoode99d63292015-04-13 08:39:25 -0700774
danakj75afea02016-04-25 20:36:04 -0700775void MidiManagerAlsa::AlsaSeqState::Client::AddPort(
776 int addr,
777 std::unique_ptr<Port> port) {
limasdfe59d0392015-11-19 20:28:57 -0800778 ports_[addr] = std::move(port);
agoode99d63292015-04-13 08:39:25 -0700779}
780
781void MidiManagerAlsa::AlsaSeqState::Client::RemovePort(int addr) {
mgiucad9af8452015-06-25 02:11:57 -0700782 ports_.erase(addr);
agoode99d63292015-04-13 08:39:25 -0700783}
784
785MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
786MidiManagerAlsa::AlsaSeqState::Client::begin() const {
787 return ports_.begin();
788}
789
790MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
791MidiManagerAlsa::AlsaSeqState::Client::end() const {
792 return ports_.end();
agoodeaf6e9f52015-03-24 10:23:49 -0700793}
794
agoodeb09423b2015-05-11 11:39:57 -0700795MidiManagerAlsa::AlsaCard::AlsaCard(udev_device* dev,
agooded87fc0f2015-05-21 08:29:31 -0700796 const std::string& name,
797 const std::string& longname,
798 const std::string& driver,
agoodeb09423b2015-05-11 11:39:57 -0700799 int midi_device_count)
agooded87fc0f2015-05-21 08:29:31 -0700800 : name_(name),
801 longname_(longname),
802 driver_(driver),
803 path_(device::UdevDeviceGetPropertyValue(dev, kUdevIdPath)),
804 bus_(device::UdevDeviceGetPropertyValue(dev, kUdevIdBus)),
805 vendor_id_(
806 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendorId, kSysattrVendor)),
807 model_id_(
808 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdModelId, kSysattrModel)),
809 usb_interface_num_(
810 device::UdevDeviceGetPropertyValue(dev, kUdevIdUsbInterfaceNum)),
811 serial_(UdevDeviceGetPropertyOrSysattr(dev,
812 kUdevIdSerialShort,
813 kSysattrGuid)),
814 midi_device_count_(midi_device_count),
815 manufacturer_(ExtractManufacturerString(
816 GetVendor(dev),
817 vendor_id_,
818 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorFromDatabase),
819 name,
820 longname)) {
agoodeb09423b2015-05-11 11:39:57 -0700821}
822
agoodeb0582872015-05-20 05:22:24 -0700823MidiManagerAlsa::AlsaCard::~AlsaCard() = default;
agoodeb09423b2015-05-11 11:39:57 -0700824
agoode55a8b522015-03-08 12:40:17 -0700825// static
agoodeb09423b2015-05-11 11:39:57 -0700826std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
agoode5e4e9cd2015-03-09 12:34:24 -0700827 const std::string& udev_id_vendor,
agoode55a8b522015-03-08 12:40:17 -0700828 const std::string& udev_id_vendor_id,
829 const std::string& udev_id_vendor_from_database,
830 const std::string& alsa_name,
831 const std::string& alsa_longname) {
832 // Let's try to determine the manufacturer. Here is the ordered preference
833 // in extraction:
agoodef212b2a2015-03-19 12:53:23 -0700834 // 1. Vendor name from the hardware device string, from udev properties
835 // or sysattrs.
agoode5e4e9cd2015-03-09 12:34:24 -0700836 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
agoode55a8b522015-03-08 12:40:17 -0700837 // 3. Heuristic from ALSA.
838
agoodee83758c2015-03-23 22:07:54 -0700839 // Is the vendor string present and not just the vendor hex id?
840 if (!udev_id_vendor.empty() && (udev_id_vendor != udev_id_vendor_id)) {
agoode55a8b522015-03-08 12:40:17 -0700841 return udev_id_vendor;
842 }
843
844 // Is there a vendor string in the hardware database?
845 if (!udev_id_vendor_from_database.empty()) {
846 return udev_id_vendor_from_database;
847 }
848
849 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
850 // We assume that card longname is in the format of
851 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
852 // a manufacturer name here.
853 size_t at_index = alsa_longname.rfind(" at ");
agoodef212b2a2015-03-19 12:53:23 -0700854 if (at_index && at_index != std::string::npos) {
agoode55a8b522015-03-08 12:40:17 -0700855 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
agoodef212b2a2015-03-19 12:53:23 -0700856 if (name_index && name_index != std::string::npos)
agoode55a8b522015-03-08 12:40:17 -0700857 return alsa_longname.substr(0, name_index - 1);
858 }
859
860 // Failure.
861 return "";
862}
863
Avi Drissman3528fd02015-12-18 20:11:31 -0500864void MidiManagerAlsa::SendMidiData(uint32_t port_index,
865 const std::vector<uint8_t>& data) {
skyostil93e2ec22015-06-17 08:49:09 -0700866 DCHECK(send_thread_.task_runner()->BelongsToCurrentThread());
agoodebd4be9b2015-03-16 19:17:25 -0700867
agoodef212b2a2015-03-19 12:53:23 -0700868 snd_midi_event_t* encoder;
869 snd_midi_event_new(kSendBufferSize, &encoder);
agoode5a1aa112015-06-21 20:51:00 -0700870 for (const auto datum : data) {
agoodebd4be9b2015-03-16 19:17:25 -0700871 snd_seq_event_t event;
agoode5a1aa112015-06-21 20:51:00 -0700872 int result = snd_midi_event_encode_byte(encoder, datum, &event);
agoodebd4be9b2015-03-16 19:17:25 -0700873 if (result == 1) {
874 // Full event, send it.
agoode99d63292015-04-13 08:39:25 -0700875 base::AutoLock lock(out_ports_lock_);
876 auto it = out_ports_.find(port_index);
877 if (it != out_ports_.end()) {
878 snd_seq_ev_set_source(&event, it->second);
879 snd_seq_ev_set_subs(&event);
880 snd_seq_ev_set_direct(&event);
agoode5a1aa112015-06-21 20:51:00 -0700881 snd_seq_event_output_direct(out_client_.get(), &event);
agoode99d63292015-04-13 08:39:25 -0700882 }
agoodebd4be9b2015-03-16 19:17:25 -0700883 }
884 }
agoodef212b2a2015-03-19 12:53:23 -0700885 snd_midi_event_free(encoder);
agoodebd4be9b2015-03-16 19:17:25 -0700886}
887
888void MidiManagerAlsa::ScheduleEventLoop() {
889 event_thread_.message_loop()->PostTask(
890 FROM_HERE,
891 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
892}
893
894void MidiManagerAlsa::EventLoop() {
agoode975043d2015-05-11 00:46:17 -0700895 bool loop_again = true;
agoodebd4be9b2015-03-16 19:17:25 -0700896
agoode975043d2015-05-11 00:46:17 -0700897 struct pollfd pfd[2];
agoode5a1aa112015-06-21 20:51:00 -0700898 snd_seq_poll_descriptors(in_client_.get(), &pfd[0], 1, POLLIN);
agoode975043d2015-05-11 00:46:17 -0700899 pfd[1].fd = device::udev_monitor_get_fd(udev_monitor_.get());
900 pfd[1].events = POLLIN;
agoodebd4be9b2015-03-16 19:17:25 -0700901
agoode975043d2015-05-11 00:46:17 -0700902 int err = HANDLE_EINTR(poll(pfd, arraysize(pfd), -1));
903 if (err < 0) {
brettwf7f870f2015-06-09 11:05:24 -0700904 VLOG(1) << "poll fails: " << base::safe_strerror(errno);
agoode975043d2015-05-11 00:46:17 -0700905 loop_again = false;
agoodeaf6e9f52015-03-24 10:23:49 -0700906 } else {
agoode975043d2015-05-11 00:46:17 -0700907 if (pfd[0].revents & POLLIN) {
908 // Read available incoming MIDI data.
909 int remaining;
910 double timestamp =
911 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
912 do {
913 snd_seq_event_t* event;
agoode5a1aa112015-06-21 20:51:00 -0700914 err = snd_seq_event_input(in_client_.get(), &event);
915 remaining = snd_seq_event_input_pending(in_client_.get(), 0);
agoode975043d2015-05-11 00:46:17 -0700916
917 if (err == -ENOSPC) {
918 // Handle out of space error.
919 VLOG(1) << "snd_seq_event_input detected buffer overrun";
920 // We've lost events: check another way to see if we need to shut
921 // down.
922 base::AutoLock lock(shutdown_lock_);
923 if (event_thread_shutdown_)
924 loop_again = false;
925 } else if (err == -EAGAIN) {
926 // We've read all the data.
927 } else if (err < 0) {
928 // Handle other errors.
929 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
930 // TODO(agoode): Use RecordAction() or similar to log this.
931 loop_again = false;
932 } else if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
933 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
934 // Handle announce events.
935 switch (event->type) {
936 case SND_SEQ_EVENT_PORT_START:
937 // Don't use SND_SEQ_EVENT_CLIENT_START because the
938 // client name may not be set by the time we query
939 // it. It should be set by the time ports are made.
940 ProcessClientStartEvent(event->data.addr.client);
941 ProcessPortStartEvent(event->data.addr);
942 break;
943 case SND_SEQ_EVENT_CLIENT_EXIT:
944 // Check for disconnection of our "out" client. This means "shut
945 // down".
946 if (event->data.addr.client == out_client_id_) {
947 loop_again = false;
948 remaining = 0;
949 } else
950 ProcessClientExitEvent(event->data.addr);
951 break;
952 case SND_SEQ_EVENT_PORT_EXIT:
953 ProcessPortExitEvent(event->data.addr);
954 break;
955 }
956 } else {
957 // Normal operation.
958 ProcessSingleEvent(event, timestamp);
959 }
960 } while (remaining > 0);
961 }
962 if (pfd[1].revents & POLLIN) {
963 device::ScopedUdevDevicePtr dev(
964 device::udev_monitor_receive_device(udev_monitor_.get()));
965 if (dev.get())
966 ProcessUdevEvent(dev.get());
967 else
968 VLOG(1) << "udev_monitor_receive_device fails";
969 }
agoodebd4be9b2015-03-16 19:17:25 -0700970 }
971
agoodebd4be9b2015-03-16 19:17:25 -0700972 // Do again.
agoode975043d2015-05-11 00:46:17 -0700973 if (loop_again)
974 ScheduleEventLoop();
agoodebd4be9b2015-03-16 19:17:25 -0700975}
976
977void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
978 double timestamp) {
agoode99d63292015-04-13 08:39:25 -0700979 auto source_it =
980 source_map_.find(AddrToInt(event->source.client, event->source.port));
agoodebd4be9b2015-03-16 19:17:25 -0700981 if (source_it != source_map_.end()) {
Avi Drissman3528fd02015-12-18 20:11:31 -0500982 uint32_t source = source_it->second;
agoodebd4be9b2015-03-16 19:17:25 -0700983 if (event->type == SND_SEQ_EVENT_SYSEX) {
984 // Special! Variable-length sysex.
Avi Drissman3528fd02015-12-18 20:11:31 -0500985 ReceiveMidiData(source, static_cast<const uint8_t*>(event->data.ext.ptr),
agoodebd4be9b2015-03-16 19:17:25 -0700986 event->data.ext.len, timestamp);
987 } else {
988 // Otherwise, decode this and send that on.
989 unsigned char buf[12];
agoode5a1aa112015-06-21 20:51:00 -0700990 long count =
991 snd_midi_event_decode(decoder_.get(), buf, sizeof(buf), event);
agoodebd4be9b2015-03-16 19:17:25 -0700992 if (count <= 0) {
993 if (count != -ENOENT) {
994 // ENOENT means that it's not a MIDI message, which is not an
995 // error, but other negative values are errors for us.
996 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
997 // TODO(agoode): Record this failure.
998 }
999 } else {
1000 ReceiveMidiData(source, buf, count, timestamp);
1001 }
1002 }
1003 }
1004}
1005
agoode99d63292015-04-13 08:39:25 -07001006void MidiManagerAlsa::ProcessClientStartEvent(int client_id) {
1007 // Ignore if client is already started.
1008 if (alsa_seq_state_.ClientStarted(client_id))
1009 return;
1010
1011 snd_seq_client_info_t* client_info;
1012 snd_seq_client_info_alloca(&client_info);
agoode5a1aa112015-06-21 20:51:00 -07001013 int err =
1014 snd_seq_get_any_client_info(in_client_.get(), client_id, client_info);
agoode99d63292015-04-13 08:39:25 -07001015 if (err != 0)
1016 return;
1017
1018 // Skip our own clients.
1019 if ((client_id == in_client_id_) || (client_id == out_client_id_))
1020 return;
1021
1022 // Update our view of ALSA seq state.
1023 alsa_seq_state_.ClientStart(client_id,
1024 snd_seq_client_info_get_name(client_info),
1025 snd_seq_client_info_get_type(client_info));
1026
1027 // Generate Web MIDI events.
1028 UpdatePortStateAndGenerateEvents();
1029}
1030
1031void MidiManagerAlsa::ProcessPortStartEvent(const snd_seq_addr_t& addr) {
1032 snd_seq_port_info_t* port_info;
1033 snd_seq_port_info_alloca(&port_info);
agoode5a1aa112015-06-21 20:51:00 -07001034 int err = snd_seq_get_any_port_info(in_client_.get(), addr.client, addr.port,
1035 port_info);
agoode99d63292015-04-13 08:39:25 -07001036 if (err != 0)
1037 return;
1038
1039 unsigned int caps = snd_seq_port_info_get_capability(port_info);
1040 bool input = (caps & kRequiredInputPortCaps) == kRequiredInputPortCaps;
1041 bool output = (caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps;
1042 AlsaSeqState::PortDirection direction;
1043 if (input && output)
1044 direction = AlsaSeqState::PortDirection::kDuplex;
1045 else if (input)
1046 direction = AlsaSeqState::PortDirection::kInput;
1047 else if (output)
1048 direction = AlsaSeqState::PortDirection::kOutput;
1049 else
1050 return;
1051
1052 // Update our view of ALSA seq state.
1053 alsa_seq_state_.PortStart(
1054 addr.client, addr.port, snd_seq_port_info_get_name(port_info), direction,
1055 snd_seq_port_info_get_type(port_info) & SND_SEQ_PORT_TYPE_MIDI_GENERIC);
1056 // Generate Web MIDI events.
1057 UpdatePortStateAndGenerateEvents();
1058}
1059
1060void MidiManagerAlsa::ProcessClientExitEvent(const snd_seq_addr_t& addr) {
1061 // Update our view of ALSA seq state.
1062 alsa_seq_state_.ClientExit(addr.client);
1063 // Generate Web MIDI events.
1064 UpdatePortStateAndGenerateEvents();
1065}
1066
1067void MidiManagerAlsa::ProcessPortExitEvent(const snd_seq_addr_t& addr) {
1068 // Update our view of ALSA seq state.
1069 alsa_seq_state_.PortExit(addr.client, addr.port);
1070 // Generate Web MIDI events.
1071 UpdatePortStateAndGenerateEvents();
1072}
1073
agoode975043d2015-05-11 00:46:17 -07001074void MidiManagerAlsa::ProcessUdevEvent(udev_device* dev) {
1075 // Only card devices have this property set, and only when they are
1076 // fully initialized.
1077 if (!device::udev_device_get_property_value(dev,
1078 kUdevPropertySoundInitialized))
1079 return;
1080
1081 // Get the action. If no action, then we are doing first time enumeration
1082 // and the device is treated as new.
1083 const char* action = device::udev_device_get_action(dev);
1084 if (!action)
1085 action = kUdevActionChange;
1086
1087 if (strcmp(action, kUdevActionChange) == 0) {
agoodeb09423b2015-05-11 11:39:57 -07001088 AddCard(dev);
1089 // Generate Web MIDI events.
1090 UpdatePortStateAndGenerateEvents();
agoode975043d2015-05-11 00:46:17 -07001091 } else if (strcmp(action, kUdevActionRemove) == 0) {
agoodeb09423b2015-05-11 11:39:57 -07001092 RemoveCard(GetCardNumber(dev));
1093 // Generate Web MIDI events.
1094 UpdatePortStateAndGenerateEvents();
agoode975043d2015-05-11 00:46:17 -07001095 }
1096}
1097
agoodeb09423b2015-05-11 11:39:57 -07001098void MidiManagerAlsa::AddCard(udev_device* dev) {
1099 int number = GetCardNumber(dev);
1100 if (number == -1)
1101 return;
1102
1103 RemoveCard(number);
1104
1105 snd_ctl_card_info_t* card;
1106 snd_hwdep_info_t* hwdep;
1107 snd_ctl_card_info_alloca(&card);
1108 snd_hwdep_info_alloca(&hwdep);
1109 const std::string id = base::StringPrintf("hw:CARD=%i", number);
1110 snd_ctl_t* handle;
1111 int err = snd_ctl_open(&handle, id.c_str(), 0);
1112 if (err != 0) {
1113 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
1114 return;
1115 }
1116 err = snd_ctl_card_info(handle, card);
1117 if (err != 0) {
1118 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
1119 snd_ctl_close(handle);
1120 return;
1121 }
1122 std::string name = snd_ctl_card_info_get_name(card);
1123 std::string longname = snd_ctl_card_info_get_longname(card);
1124 std::string driver = snd_ctl_card_info_get_driver(card);
1125
1126 // Count rawmidi devices (not subdevices).
1127 int midi_count = 0;
1128 for (int device = -1;
1129 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;)
1130 ++midi_count;
1131
1132 // Count any hwdep synths that become MIDI devices outside of rawmidi.
1133 //
1134 // Explanation:
1135 // Any kernel driver can create an ALSA client (visible to us).
1136 // With modern hardware, only rawmidi devices do this. Kernel
1137 // drivers create rawmidi devices and the rawmidi subsystem makes
1138 // the seq clients. But the OPL3 driver is special, it does not
1139 // make a rawmidi device but a seq client directly. (This is the
1140 // only one to worry about in the kernel code, as of 2015-03-23.)
1141 //
1142 // OPL3 is very old (but still possible to get in new
1143 // hardware). It is unlikely that new drivers would not use
1144 // rawmidi and defeat our heuristic.
1145 //
1146 // Longer term, support should be added in the kernel to expose a
1147 // direct link from card->client (or client->card) so that all
1148 // these heuristics will be obsolete. Once that is there, we can
1149 // assume our old heuristics will work on old kernels and the new
1150 // robust code will be used on new. Then we will not need to worry
1151 // about changes to kernel internals breaking our code.
1152 // See the TODO above at kMinimumClientIdForCards.
1153 for (int device = -1;
1154 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) {
1155 err = snd_ctl_hwdep_info(handle, hwdep);
1156 if (err != 0) {
1157 VLOG(1) << "snd_ctl_hwdep_info fails: " << snd_strerror(err);
1158 continue;
1159 }
1160 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep);
1161 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 ||
1162 iface == SND_HWDEP_IFACE_OPL4)
1163 ++midi_count;
1164 }
1165 snd_ctl_close(handle);
1166
mgiucad9af8452015-06-25 02:11:57 -07001167 if (midi_count > 0) {
danakj75afea02016-04-25 20:36:04 -07001168 std::unique_ptr<AlsaCard> card(
mgiucad9af8452015-06-25 02:11:57 -07001169 new AlsaCard(dev, name, longname, driver, midi_count));
limasdfe59d0392015-11-19 20:28:57 -08001170 alsa_cards_.insert(std::make_pair(number, std::move(card)));
mgiucad9af8452015-06-25 02:11:57 -07001171 alsa_card_midi_count_ += midi_count;
1172 }
agoodeb09423b2015-05-11 11:39:57 -07001173}
1174
1175void MidiManagerAlsa::RemoveCard(int number) {
1176 auto it = alsa_cards_.find(number);
1177 if (it == alsa_cards_.end())
1178 return;
1179
1180 alsa_card_midi_count_ -= it->second->midi_device_count();
agoodeb09423b2015-05-11 11:39:57 -07001181 alsa_cards_.erase(it);
1182}
1183
agoode99d63292015-04-13 08:39:25 -07001184void MidiManagerAlsa::UpdatePortStateAndGenerateEvents() {
agoodeb09423b2015-05-11 11:39:57 -07001185 // Verify that our information from ALSA and udev are in sync. If
1186 // not, we cannot generate events right now.
1187 if (alsa_card_midi_count_ != alsa_seq_state_.card_client_count())
1188 return;
1189
agoode99d63292015-04-13 08:39:25 -07001190 // Generate new port state.
agoodeb09423b2015-05-11 11:39:57 -07001191 auto new_port_state = alsa_seq_state_.ToMidiPortState(alsa_cards_);
agoode99d63292015-04-13 08:39:25 -07001192
1193 // Disconnect any connected old ports that are now missing.
agoode5ebc4932015-12-01 08:25:12 -08001194 for (auto& old_port : port_state_) {
agoode99d63292015-04-13 08:39:25 -07001195 if (old_port->connected() &&
1196 (new_port_state->FindConnected(*old_port) == new_port_state->end())) {
1197 old_port->set_connected(false);
Avi Drissman3528fd02015-12-18 20:11:31 -05001198 uint32_t web_port_index = old_port->web_port_index();
agoode99d63292015-04-13 08:39:25 -07001199 switch (old_port->type()) {
1200 case MidiPort::Type::kInput:
1201 source_map_.erase(
1202 AddrToInt(old_port->client_id(), old_port->port_id()));
1203 SetInputPortState(web_port_index, MIDI_PORT_DISCONNECTED);
1204 break;
1205 case MidiPort::Type::kOutput:
1206 DeleteAlsaOutputPort(web_port_index);
1207 SetOutputPortState(web_port_index, MIDI_PORT_DISCONNECTED);
1208 break;
1209 }
1210 }
1211 }
1212
1213 // Reconnect or add new ports.
1214 auto it = new_port_state->begin();
1215 while (it != new_port_state->end()) {
agoode5ebc4932015-12-01 08:25:12 -08001216 auto& new_port = *it;
agoode99d63292015-04-13 08:39:25 -07001217 auto old_port = port_state_.Find(*new_port);
1218 if (old_port == port_state_.end()) {
1219 // Add new port.
agoode5ebc4932015-12-01 08:25:12 -08001220 const auto& opaque_key = new_port->OpaqueKey();
1221 const auto& manufacturer = new_port->manufacturer();
1222 const auto& port_name = new_port->port_name();
1223 const auto& version = new_port->version();
1224 const auto& type = new_port->type();
1225 const auto& client_id = new_port->client_id();
1226 const auto& port_id = new_port->port_id();
1227
dchengc2aeece2015-12-27 00:54:00 -08001228 uint32_t web_port_index = port_state_.push_back(std::move(new_port));
agoode5ebc4932015-12-01 08:25:12 -08001229 it = new_port_state->erase(it);
1230
1231 MidiPortInfo info(opaque_key, manufacturer, port_name, version,
agoode99d63292015-04-13 08:39:25 -07001232 MIDI_PORT_OPENED);
agoode5ebc4932015-12-01 08:25:12 -08001233 switch (type) {
agoode99d63292015-04-13 08:39:25 -07001234 case MidiPort::Type::kInput:
agoode5ebc4932015-12-01 08:25:12 -08001235 if (Subscribe(web_port_index, client_id, port_id))
agoode99d63292015-04-13 08:39:25 -07001236 AddInputPort(info);
1237 break;
agoode99d63292015-04-13 08:39:25 -07001238 case MidiPort::Type::kOutput:
agoode5ebc4932015-12-01 08:25:12 -08001239 if (CreateAlsaOutputPort(web_port_index, client_id, port_id))
agoode99d63292015-04-13 08:39:25 -07001240 AddOutputPort(info);
1241 break;
1242 }
agoode99d63292015-04-13 08:39:25 -07001243 } else if (!(*old_port)->connected()) {
1244 // Reconnect.
Avi Drissman3528fd02015-12-18 20:11:31 -05001245 uint32_t web_port_index = (*old_port)->web_port_index();
agoode99d63292015-04-13 08:39:25 -07001246 (*old_port)->Update(new_port->path(), new_port->client_id(),
1247 new_port->port_id(), new_port->client_name(),
1248 new_port->port_name(), new_port->manufacturer(),
1249 new_port->version());
1250 switch ((*old_port)->type()) {
1251 case MidiPort::Type::kInput:
1252 if (Subscribe(web_port_index, (*old_port)->client_id(),
1253 (*old_port)->port_id()))
1254 SetInputPortState(web_port_index, MIDI_PORT_OPENED);
1255 break;
agoode99d63292015-04-13 08:39:25 -07001256 case MidiPort::Type::kOutput:
1257 if (CreateAlsaOutputPort(web_port_index, (*old_port)->client_id(),
1258 (*old_port)->port_id()))
1259 SetOutputPortState(web_port_index, MIDI_PORT_OPENED);
1260 break;
1261 }
1262 (*old_port)->set_connected(true);
1263 ++it;
1264 } else {
1265 ++it;
1266 }
1267 }
1268}
1269
agoode975043d2015-05-11 00:46:17 -07001270// TODO(agoode): return false on failure.
agoode99d63292015-04-13 08:39:25 -07001271void MidiManagerAlsa::EnumerateAlsaPorts() {
1272 snd_seq_client_info_t* client_info;
1273 snd_seq_client_info_alloca(&client_info);
1274 snd_seq_port_info_t* port_info;
1275 snd_seq_port_info_alloca(&port_info);
1276
1277 // Enumerate clients.
1278 snd_seq_client_info_set_client(client_info, -1);
agoode5a1aa112015-06-21 20:51:00 -07001279 while (!snd_seq_query_next_client(in_client_.get(), client_info)) {
agoode99d63292015-04-13 08:39:25 -07001280 int client_id = snd_seq_client_info_get_client(client_info);
1281 ProcessClientStartEvent(client_id);
1282
1283 // Enumerate ports.
1284 snd_seq_port_info_set_client(port_info, client_id);
1285 snd_seq_port_info_set_port(port_info, -1);
agoode5a1aa112015-06-21 20:51:00 -07001286 while (!snd_seq_query_next_port(in_client_.get(), port_info)) {
agoode99d63292015-04-13 08:39:25 -07001287 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
1288 ProcessPortStartEvent(*addr);
1289 }
1290 }
1291}
1292
agoode975043d2015-05-11 00:46:17 -07001293bool MidiManagerAlsa::EnumerateUdevCards() {
1294 int err;
1295
1296 device::ScopedUdevEnumeratePtr enumerate(
1297 device::udev_enumerate_new(udev_.get()));
1298 if (!enumerate.get()) {
1299 VLOG(1) << "udev_enumerate_new fails";
1300 return false;
1301 }
1302
1303 err = device::udev_enumerate_add_match_subsystem(enumerate.get(),
1304 kUdevSubsystemSound);
1305 if (err) {
1306 VLOG(1) << "udev_enumerate_add_match_subsystem fails: "
brettwf7f870f2015-06-09 11:05:24 -07001307 << base::safe_strerror(-err);
agoode975043d2015-05-11 00:46:17 -07001308 return false;
1309 }
1310
1311 err = device::udev_enumerate_scan_devices(enumerate.get());
1312 if (err) {
brettwf7f870f2015-06-09 11:05:24 -07001313 VLOG(1) << "udev_enumerate_scan_devices fails: "
1314 << base::safe_strerror(-err);
agoode975043d2015-05-11 00:46:17 -07001315 return false;
1316 }
1317
1318 udev_list_entry* list_entry;
1319 auto* devices = device::udev_enumerate_get_list_entry(enumerate.get());
1320 udev_list_entry_foreach(list_entry, devices) {
1321 const char* path = device::udev_list_entry_get_name(list_entry);
1322 device::ScopedUdevDevicePtr dev(
1323 device::udev_device_new_from_syspath(udev_.get(), path));
1324 if (dev.get())
1325 ProcessUdevEvent(dev.get());
1326 }
1327
1328 return true;
1329}
1330
Avi Drissman3528fd02015-12-18 20:11:31 -05001331bool MidiManagerAlsa::CreateAlsaOutputPort(uint32_t port_index,
agoode99d63292015-04-13 08:39:25 -07001332 int client_id,
1333 int port_id) {
1334 // Create the port.
1335 int out_port = snd_seq_create_simple_port(
agoode5a1aa112015-06-21 20:51:00 -07001336 out_client_.get(), NULL, kCreateOutputPortCaps, kCreatePortType);
agoode99d63292015-04-13 08:39:25 -07001337 if (out_port < 0) {
1338 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(out_port);
1339 return false;
1340 }
1341 // Activate port subscription.
1342 snd_seq_port_subscribe_t* subs;
1343 snd_seq_port_subscribe_alloca(&subs);
1344 snd_seq_addr_t sender;
1345 sender.client = out_client_id_;
1346 sender.port = out_port;
1347 snd_seq_port_subscribe_set_sender(subs, &sender);
1348 snd_seq_addr_t dest;
1349 dest.client = client_id;
1350 dest.port = port_id;
1351 snd_seq_port_subscribe_set_dest(subs, &dest);
agoode5a1aa112015-06-21 20:51:00 -07001352 int err = snd_seq_subscribe_port(out_client_.get(), subs);
agoode99d63292015-04-13 08:39:25 -07001353 if (err != 0) {
1354 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
agoode5a1aa112015-06-21 20:51:00 -07001355 snd_seq_delete_simple_port(out_client_.get(), out_port);
agoode99d63292015-04-13 08:39:25 -07001356 return false;
1357 }
1358
1359 // Update our map.
1360 base::AutoLock lock(out_ports_lock_);
1361 out_ports_[port_index] = out_port;
1362 return true;
1363}
1364
Avi Drissman3528fd02015-12-18 20:11:31 -05001365void MidiManagerAlsa::DeleteAlsaOutputPort(uint32_t port_index) {
agoode99d63292015-04-13 08:39:25 -07001366 base::AutoLock lock(out_ports_lock_);
1367 auto it = out_ports_.find(port_index);
1368 if (it == out_ports_.end())
1369 return;
1370
1371 int alsa_port = it->second;
agoode5a1aa112015-06-21 20:51:00 -07001372 snd_seq_delete_simple_port(out_client_.get(), alsa_port);
agoode99d63292015-04-13 08:39:25 -07001373 out_ports_.erase(it);
1374}
1375
Avi Drissman3528fd02015-12-18 20:11:31 -05001376bool MidiManagerAlsa::Subscribe(uint32_t port_index,
1377 int client_id,
1378 int port_id) {
agoode99d63292015-04-13 08:39:25 -07001379 // Activate port subscription.
1380 snd_seq_port_subscribe_t* subs;
1381 snd_seq_port_subscribe_alloca(&subs);
1382 snd_seq_addr_t sender;
1383 sender.client = client_id;
1384 sender.port = port_id;
1385 snd_seq_port_subscribe_set_sender(subs, &sender);
1386 snd_seq_addr_t dest;
1387 dest.client = in_client_id_;
1388 dest.port = in_port_id_;
1389 snd_seq_port_subscribe_set_dest(subs, &dest);
agoode5a1aa112015-06-21 20:51:00 -07001390 int err = snd_seq_subscribe_port(in_client_.get(), subs);
agoode99d63292015-04-13 08:39:25 -07001391 if (err != 0) {
1392 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
1393 return false;
1394 }
1395
1396 // Update our map.
1397 source_map_[AddrToInt(client_id, port_id)] = port_index;
1398 return true;
1399}
1400
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +00001401MidiManager* MidiManager::Create() {
1402 return new MidiManagerAlsa();
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +00001403}
1404
toyoshime147c5e2015-05-07 21:58:31 -07001405} // namespace midi
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +00001406} // namespace media