blob: d99751bff01be9161f404b4681d980b7b54d839d [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"
fdoray8baaff02016-08-25 08:36:37 -070024#include "base/single_thread_task_runner.h"
agoodef212b2a2015-03-19 12:53:23 -070025#include "base/strings/string_number_conversions.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000026#include "base/strings/stringprintf.h"
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000027#include "base/time/time.h"
agoodef212b2a2015-03-19 12:53:23 -070028#include "crypto/sha2.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000029#include "media/midi/midi_port_info.h"
30
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
toyoshimec2570a2016-10-21 02:15:27 -070035using mojom::PortState;
36using mojom::Result;
toyoshim2f3a48f2016-10-17 01:54:13 -070037
agoode@chromium.org25227512014-06-08 05:12:05 +000038// Per-output buffer. This can be smaller, but then large sysex messages
39// will be (harmlessly) split across multiple seq events. This should
40// not have any real practical effect, except perhaps to slightly reorder
41// realtime messages with respect to sysex.
42const size_t kSendBufferSize = 256;
43
agoodeb09423b2015-05-11 11:39:57 -070044// Minimum client id for which we will have ALSA card devices for. When we
45// are searching for card devices (used to get the path, id, and manufacturer),
46// we don't want to get confused by kernel clients that do not have a card.
47// See seq_clientmgr.c in the ALSA code for this.
48// TODO(agoode): Add proper client -> card export from the kernel to avoid
49// hardcoding.
50const int kMinimumClientIdForCards = 16;
51
agoodef212b2a2015-03-19 12:53:23 -070052// ALSA constants.
53const char kAlsaHw[] = "hw";
54
agoode975043d2015-05-11 00:46:17 -070055// udev constants.
56const char kUdev[] = "udev";
57const char kUdevSubsystemSound[] = "sound";
58const char kUdevPropertySoundInitialized[] = "SOUND_INITIALIZED";
59const char kUdevActionChange[] = "change";
60const char kUdevActionRemove[] = "remove";
61
agoodeb09423b2015-05-11 11:39:57 -070062const char kUdevIdVendor[] = "ID_VENDOR";
63const char kUdevIdVendorEnc[] = "ID_VENDOR_ENC";
64const char kUdevIdVendorFromDatabase[] = "ID_VENDOR_FROM_DATABASE";
65const char kUdevIdVendorId[] = "ID_VENDOR_ID";
66const char kUdevIdModelId[] = "ID_MODEL_ID";
67const char kUdevIdBus[] = "ID_BUS";
68const char kUdevIdPath[] = "ID_PATH";
69const char kUdevIdUsbInterfaceNum[] = "ID_USB_INTERFACE_NUM";
70const char kUdevIdSerialShort[] = "ID_SERIAL_SHORT";
71
72const char kSysattrVendorName[] = "vendor_name";
73const char kSysattrVendor[] = "vendor";
74const char kSysattrModel[] = "model";
75const char kSysattrGuid[] = "guid";
76
77const char kCardSyspath[] = "/card";
78
agoode@chromium.org25227512014-06-08 05:12:05 +000079// Constants for the capabilities we search for in inputs and outputs.
80// See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
81const unsigned int kRequiredInputPortCaps =
82 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
83const unsigned int kRequiredOutputPortCaps =
84 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
85
agoode99d63292015-04-13 08:39:25 -070086const unsigned int kCreateOutputPortCaps =
87 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT;
88const unsigned int kCreateInputPortCaps =
89 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT;
90const unsigned int kCreatePortType =
91 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
agoode@chromium.org25227512014-06-08 05:12:05 +000092
agoode99d63292015-04-13 08:39:25 -070093int AddrToInt(int client, int port) {
94 return (client << 8) | port;
agoodef212b2a2015-03-19 12:53:23 -070095}
agoodef212b2a2015-03-19 12:53:23 -070096
agoodeb09423b2015-05-11 11:39:57 -070097// Returns true if this client has an ALSA card associated with it.
98bool IsCardClient(snd_seq_client_type_t type, int client_id) {
99 return (type == SND_SEQ_KERNEL_CLIENT) &&
100 (client_id >= kMinimumClientIdForCards);
101}
102
103// TODO(agoode): Move this to device/udev_linux.
104const std::string UdevDeviceGetPropertyOrSysattr(
105 struct udev_device* udev_device,
106 const char* property_key,
107 const char* sysattr_key) {
108 // First try the property.
109 std::string value =
110 device::UdevDeviceGetPropertyValue(udev_device, property_key);
111
112 // If no property, look for sysattrs and walk up the parent devices too.
113 while (value.empty() && udev_device) {
114 value = device::UdevDeviceGetSysattrValue(udev_device, sysattr_key);
115 udev_device = device::udev_device_get_parent(udev_device);
116 }
117 return value;
118}
119
120int GetCardNumber(udev_device* dev) {
121 const char* syspath = device::udev_device_get_syspath(dev);
122 if (!syspath)
123 return -1;
124
125 std::string syspath_str(syspath);
126 size_t i = syspath_str.rfind(kCardSyspath);
127 if (i == std::string::npos)
128 return -1;
129
130 int number;
131 if (!base::StringToInt(syspath_str.substr(i + strlen(kCardSyspath)), &number))
132 return -1;
133 return number;
134}
135
agooded87fc0f2015-05-21 08:29:31 -0700136std::string GetVendor(udev_device* dev) {
137 // Try to get the vendor string. Sometimes it is encoded.
138 std::string vendor = device::UdevDecodeString(
139 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorEnc));
140 // Sometimes it is not encoded.
141 if (vendor.empty())
142 vendor =
143 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendor, kSysattrVendorName);
144 return vendor;
145}
146
agoode8caab0b2015-03-23 18:48:02 -0700147void SetStringIfNonEmpty(base::DictionaryValue* value,
148 const std::string& path,
149 const std::string& in_value) {
150 if (!in_value.empty())
151 value->SetString(path, in_value);
152}
153
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000154} // namespace
155
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000156MidiManagerAlsa::MidiManagerAlsa()
agoodeb2ead822016-03-11 12:14:35 -0800157 : event_thread_("MidiEventThread"), send_thread_("MidiSendThread") {}
158
159MidiManagerAlsa::~MidiManagerAlsa() {
160 // Take lock to ensure that the members initialized on the IO thread
161 // are not destructed here.
162 base::AutoLock lock(lazy_init_member_lock_);
163
toyoshim131beb52016-07-25 00:42:41 -0700164 // Extra CHECK to verify all members are already reset.
165 CHECK(!initialization_thread_checker_);
166 CHECK(!in_client_);
167 CHECK(!out_client_);
168 CHECK(!decoder_);
169 CHECK(!udev_);
170 CHECK(!udev_monitor_);
171
172 CHECK(!send_thread_.IsRunning());
173 CHECK(!event_thread_.IsRunning());
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000174}
175
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000176void MidiManagerAlsa::StartInitialization() {
agoodeb2ead822016-03-11 12:14:35 -0800177 base::AutoLock lock(lazy_init_member_lock_);
agoode5a1aa112015-06-21 20:51:00 -0700178
agoodeb2ead822016-03-11 12:14:35 -0800179 initialization_thread_checker_.reset(new base::ThreadChecker());
180
181 // Create client handles.
182 snd_seq_t* tmp_seq = nullptr;
183 int err =
184 snd_seq_open(&tmp_seq, kAlsaHw, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
agoode@chromium.org25227512014-06-08 05:12:05 +0000185 if (err != 0) {
186 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700187 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000188 }
agoodeb2ead822016-03-11 12:14:35 -0800189 ScopedSndSeqPtr in_client(tmp_seq);
190 tmp_seq = nullptr;
191 in_client_id_ = snd_seq_client_id(in_client.get());
192
193 err = snd_seq_open(&tmp_seq, kAlsaHw, SND_SEQ_OPEN_OUTPUT, 0);
194 if (err != 0) {
195 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
196 return CompleteInitialization(Result::INITIALIZATION_ERROR);
197 }
198 ScopedSndSeqPtr out_client(tmp_seq);
199 tmp_seq = nullptr;
200 out_client_id_ = snd_seq_client_id(out_client.get());
agoode@chromium.org25227512014-06-08 05:12:05 +0000201
202 // Name the clients.
agoodeb2ead822016-03-11 12:14:35 -0800203 err = snd_seq_set_client_name(in_client.get(), "Chrome (input)");
agoode@chromium.org25227512014-06-08 05:12:05 +0000204 if (err != 0) {
205 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700206 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000207 }
agoodeb2ead822016-03-11 12:14:35 -0800208 err = snd_seq_set_client_name(out_client.get(), "Chrome (output)");
agoode@chromium.org25227512014-06-08 05:12:05 +0000209 if (err != 0) {
210 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700211 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000212 }
213
214 // Create input port.
agoode99d63292015-04-13 08:39:25 -0700215 in_port_id_ = snd_seq_create_simple_port(
agoodeb2ead822016-03-11 12:14:35 -0800216 in_client.get(), NULL, kCreateInputPortCaps, kCreatePortType);
agoode99d63292015-04-13 08:39:25 -0700217 if (in_port_id_ < 0) {
218 VLOG(1) << "snd_seq_create_simple_port fails: "
219 << snd_strerror(in_port_id_);
toyoshimf1b88962015-07-09 14:14:51 -0700220 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000221 }
222
223 // Subscribe to the announce port.
224 snd_seq_port_subscribe_t* subs;
225 snd_seq_port_subscribe_alloca(&subs);
226 snd_seq_addr_t announce_sender;
227 snd_seq_addr_t announce_dest;
228 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
229 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
agoode99d63292015-04-13 08:39:25 -0700230 announce_dest.client = in_client_id_;
231 announce_dest.port = in_port_id_;
agoode@chromium.org25227512014-06-08 05:12:05 +0000232 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
233 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
agoodeb2ead822016-03-11 12:14:35 -0800234 err = snd_seq_subscribe_port(in_client.get(), subs);
agoode@chromium.org25227512014-06-08 05:12:05 +0000235 if (err != 0) {
236 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
237 << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700238 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000239 }
240
agoodeb2ead822016-03-11 12:14:35 -0800241 // Initialize decoder.
242 snd_midi_event_t* tmp_decoder = nullptr;
243 snd_midi_event_new(0, &tmp_decoder);
244 ScopedSndMidiEventPtr decoder(tmp_decoder);
245 tmp_decoder = nullptr;
246 snd_midi_event_no_status(decoder.get(), 1);
agoode@chromium.org25227512014-06-08 05:12:05 +0000247
agoodeb2ead822016-03-11 12:14:35 -0800248 // Initialize udev and monitor.
249 device::ScopedUdevPtr udev(device::udev_new());
250 device::ScopedUdevMonitorPtr udev_monitor(
251 device::udev_monitor_new_from_netlink(udev.get(), kUdev));
252 if (!udev_monitor.get()) {
agoode975043d2015-05-11 00:46:17 -0700253 VLOG(1) << "udev_monitor_new_from_netlink fails";
toyoshimf1b88962015-07-09 14:14:51 -0700254 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700255 }
256 err = device::udev_monitor_filter_add_match_subsystem_devtype(
agoodeb2ead822016-03-11 12:14:35 -0800257 udev_monitor.get(), kUdevSubsystemSound, nullptr);
agoode975043d2015-05-11 00:46:17 -0700258 if (err != 0) {
259 VLOG(1) << "udev_monitor_add_match_subsystem fails: "
brettwf7f870f2015-06-09 11:05:24 -0700260 << base::safe_strerror(-err);
toyoshimf1b88962015-07-09 14:14:51 -0700261 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700262 }
agoodeb2ead822016-03-11 12:14:35 -0800263 err = device::udev_monitor_enable_receiving(udev_monitor.get());
agoode975043d2015-05-11 00:46:17 -0700264 if (err != 0) {
brettwf7f870f2015-06-09 11:05:24 -0700265 VLOG(1) << "udev_monitor_enable_receiving fails: "
266 << base::safe_strerror(-err);
toyoshimf1b88962015-07-09 14:14:51 -0700267 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700268 }
269
agoodeb2ead822016-03-11 12:14:35 -0800270 // Success! Now, initialize members from the temporaries. Do not
271 // initialize these earlier, since they need to be destroyed by the
272 // thread that calls Finalize(), not the destructor thread (and we
273 // check this in the destructor).
274 in_client_.reset(in_client.release());
275 out_client_.reset(out_client.release());
276 decoder_.reset(decoder.release());
277 udev_.reset(udev.release());
agoode740f5222016-07-06 23:46:32 -0700278 udev_monitor_.reset(udev_monitor.release());
agoodeb2ead822016-03-11 12:14:35 -0800279
280 // Generate hotplug events for existing ports.
281 // TODO(agoode): Check the return value for failure.
282 EnumerateAlsaPorts();
283
284 // Generate hotplug events for existing udev devices. This must be done
285 // after udev_monitor_enable_receiving() is called. See the algorithm
286 // at http://www.signal11.us/oss/udev/.
agoode975043d2015-05-11 00:46:17 -0700287 EnumerateUdevCards();
288
agoodeb2ead822016-03-11 12:14:35 -0800289 // Start processing events. Don't do this before enumeration of both
290 // ALSA and udev.
agoode@chromium.org25227512014-06-08 05:12:05 +0000291 event_thread_.Start();
fdoray8baaff02016-08-25 08:36:37 -0700292 event_thread_.task_runner()->PostTask(
agoode@chromium.org25227512014-06-08 05:12:05 +0000293 FROM_HERE,
agoodebd4be9b2015-03-16 19:17:25 -0700294 base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this)));
agoodeb2ead822016-03-11 12:14:35 -0800295 send_thread_.Start();
agoode@chromium.org25227512014-06-08 05:12:05 +0000296
toyoshimf1b88962015-07-09 14:14:51 -0700297 CompleteInitialization(Result::OK);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000298}
299
toyoshim8e7d6e02015-10-06 08:47:17 -0700300void MidiManagerAlsa::Finalize() {
agoodeb2ead822016-03-11 12:14:35 -0800301 base::AutoLock lock(lazy_init_member_lock_);
302 DCHECK(initialization_thread_checker_->CalledOnValidThread());
303
toyoshim8e7d6e02015-10-06 08:47:17 -0700304 // Tell the event thread it will soon be time to shut down. This gives
305 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
306 // message is lost.
307 {
308 base::AutoLock lock(shutdown_lock_);
309 event_thread_shutdown_ = true;
310 }
311
312 // Stop the send thread.
313 send_thread_.Stop();
314
315 // Close the out client. This will trigger the event thread to stop,
316 // because of SND_SEQ_EVENT_CLIENT_EXIT.
agoodeb2ead822016-03-11 12:14:35 -0800317 out_client_.reset();
toyoshim8e7d6e02015-10-06 08:47:17 -0700318
319 // Wait for the event thread to stop.
320 event_thread_.Stop();
agoodeb2ead822016-03-11 12:14:35 -0800321
322 // Destruct the other stuff we initialized in StartInitialization().
323 udev_monitor_.reset();
324 udev_.reset();
325 decoder_.reset();
326 in_client_.reset();
327 initialization_thread_checker_.reset();
toyoshim8e7d6e02015-10-06 08:47:17 -0700328}
329
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000330void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500331 uint32_t port_index,
332 const std::vector<uint8_t>& data,
dnicoara@chromium.org9f2a6f02014-01-03 21:25:00 +0000333 double timestamp) {
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000334 base::TimeDelta delay;
335 if (timestamp != 0.0) {
336 base::TimeTicks time_to_send =
337 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
agoodebd4be9b2015-03-16 19:17:25 -0700338 timestamp * base::Time::kMicrosecondsPerSecond);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000339 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
340 }
341
fdoray8baaff02016-08-25 08:36:37 -0700342 send_thread_.task_runner()->PostDelayedTask(
agoodebd4be9b2015-03-16 19:17:25 -0700343 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData,
344 base::Unretained(this), port_index, data),
345 delay);
agoode@chromium.org25227512014-06-08 05:12:05 +0000346
347 // Acknowledge send.
fdoray8baaff02016-08-25 08:36:37 -0700348 send_thread_.task_runner()->PostTask(
agoodead116b22015-12-07 00:00:35 -0800349 FROM_HERE, base::Bind(&MidiManagerAlsa::AccumulateMidiBytesSent,
350 base::Unretained(this), client, data.size()));
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000351}
352
agooded87fc0f2015-05-21 08:29:31 -0700353MidiManagerAlsa::MidiPort::Id::Id() = default;
354
355MidiManagerAlsa::MidiPort::Id::Id(const std::string& bus,
356 const std::string& vendor_id,
357 const std::string& model_id,
358 const std::string& usb_interface_num,
359 const std::string& serial)
360 : bus_(bus),
361 vendor_id_(vendor_id),
362 model_id_(model_id),
363 usb_interface_num_(usb_interface_num),
364 serial_(serial) {
365}
366
367MidiManagerAlsa::MidiPort::Id::Id(const Id&) = default;
368
369MidiManagerAlsa::MidiPort::Id::~Id() = default;
370
371bool MidiManagerAlsa::MidiPort::Id::operator==(const Id& rhs) const {
372 return (bus_ == rhs.bus_) && (vendor_id_ == rhs.vendor_id_) &&
373 (model_id_ == rhs.model_id_) &&
374 (usb_interface_num_ == rhs.usb_interface_num_) &&
375 (serial_ == rhs.serial_);
376}
377
378bool MidiManagerAlsa::MidiPort::Id::empty() const {
379 return bus_.empty() && vendor_id_.empty() && model_id_.empty() &&
380 usb_interface_num_.empty() && serial_.empty();
381}
382
agoode99d63292015-04-13 08:39:25 -0700383MidiManagerAlsa::MidiPort::MidiPort(const std::string& path,
agooded87fc0f2015-05-21 08:29:31 -0700384 const Id& id,
agoode99d63292015-04-13 08:39:25 -0700385 int client_id,
386 int port_id,
387 int midi_device,
388 const std::string& client_name,
389 const std::string& port_name,
390 const std::string& manufacturer,
391 const std::string& version,
392 Type type)
393 : id_(id),
394 midi_device_(midi_device),
395 type_(type),
396 path_(path),
397 client_id_(client_id),
398 port_id_(port_id),
399 client_name_(client_name),
400 port_name_(port_name),
401 manufacturer_(manufacturer),
agoode5a1aa112015-06-21 20:51:00 -0700402 version_(version) {
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000403}
404
agoodeb0582872015-05-20 05:22:24 -0700405MidiManagerAlsa::MidiPort::~MidiPort() = default;
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000406
agoode99d63292015-04-13 08:39:25 -0700407// Note: keep synchronized with the MidiPort::Match* methods.
danakj75afea02016-04-25 20:36:04 -0700408std::unique_ptr<base::Value> MidiManagerAlsa::MidiPort::Value() const {
409 std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
agoode99d63292015-04-13 08:39:25 -0700410
411 std::string type;
412 switch (type_) {
413 case Type::kInput:
414 type = "input";
415 break;
agoode99d63292015-04-13 08:39:25 -0700416 case Type::kOutput:
417 type = "output";
418 break;
419 }
420 value->SetString("type", type);
421 SetStringIfNonEmpty(value.get(), "path", path_);
agoode99d63292015-04-13 08:39:25 -0700422 SetStringIfNonEmpty(value.get(), "clientName", client_name_);
423 SetStringIfNonEmpty(value.get(), "portName", port_name_);
424 value->SetInteger("clientId", client_id_);
425 value->SetInteger("portId", port_id_);
426 value->SetInteger("midiDevice", midi_device_);
427
agooded87fc0f2015-05-21 08:29:31 -0700428 // Flatten id fields.
429 SetStringIfNonEmpty(value.get(), "bus", id_.bus());
430 SetStringIfNonEmpty(value.get(), "vendorId", id_.vendor_id());
431 SetStringIfNonEmpty(value.get(), "modelId", id_.model_id());
432 SetStringIfNonEmpty(value.get(), "usbInterfaceNum", id_.usb_interface_num());
433 SetStringIfNonEmpty(value.get(), "serial", id_.serial());
434
dchengc2aeece2015-12-27 00:54:00 -0800435 return std::move(value);
agoodebd4be9b2015-03-16 19:17:25 -0700436}
agoode@chromium.org25227512014-06-08 05:12:05 +0000437
agoode99d63292015-04-13 08:39:25 -0700438std::string MidiManagerAlsa::MidiPort::JSONValue() const {
439 std::string json;
440 JSONStringValueSerializer serializer(&json);
441 serializer.Serialize(*Value().get());
442 return json;
agoodef212b2a2015-03-19 12:53:23 -0700443}
444
agoode99d63292015-04-13 08:39:25 -0700445// TODO(agoode): Do not use SHA256 here. Instead store a persistent
446// mapping and just use a UUID or other random string.
447// http://crbug.com/465320
448std::string MidiManagerAlsa::MidiPort::OpaqueKey() const {
Avi Drissman3528fd02015-12-18 20:11:31 -0500449 uint8_t hash[crypto::kSHA256Length];
agoode99d63292015-04-13 08:39:25 -0700450 crypto::SHA256HashString(JSONValue(), &hash, sizeof(hash));
451 return base::HexEncode(&hash, sizeof(hash));
agoodebd4be9b2015-03-16 19:17:25 -0700452}
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000453
agoode99d63292015-04-13 08:39:25 -0700454bool MidiManagerAlsa::MidiPort::MatchConnected(const MidiPort& query) const {
455 // Matches on:
456 // connected == true
457 // type
458 // path
459 // id
460 // client_id
461 // port_id
462 // midi_device
463 // client_name
464 // port_name
465 return connected() && (type() == query.type()) && (path() == query.path()) &&
466 (id() == query.id()) && (client_id() == query.client_id()) &&
467 (port_id() == query.port_id()) &&
468 (midi_device() == query.midi_device()) &&
469 (client_name() == query.client_name()) &&
470 (port_name() == query.port_name());
agoodebd4be9b2015-03-16 19:17:25 -0700471}
472
agoode99d63292015-04-13 08:39:25 -0700473bool MidiManagerAlsa::MidiPort::MatchCardPass1(const MidiPort& query) const {
474 // Matches on:
475 // connected == false
476 // type
477 // path
478 // id
479 // port_id
480 // midi_device
481 return MatchCardPass2(query) && (path() == query.path());
agoodebd4be9b2015-03-16 19:17:25 -0700482}
483
agoode99d63292015-04-13 08:39:25 -0700484bool MidiManagerAlsa::MidiPort::MatchCardPass2(const MidiPort& query) const {
485 // Matches on:
486 // connected == false
487 // type
488 // id
489 // port_id
490 // midi_device
491 return !connected() && (type() == query.type()) && (id() == query.id()) &&
492 (port_id() == query.port_id()) &&
493 (midi_device() == query.midi_device());
agoodef212b2a2015-03-19 12:53:23 -0700494}
495
agoode99d63292015-04-13 08:39:25 -0700496bool MidiManagerAlsa::MidiPort::MatchNoCardPass1(const MidiPort& query) const {
497 // Matches on:
498 // connected == false
499 // type
500 // path.empty(), for both this and query
501 // id.empty(), for both this and query
502 // client_id
503 // port_id
504 // client_name
505 // port_name
506 // midi_device == -1, for both this and query
507 return MatchNoCardPass2(query) && (client_id() == query.client_id());
agoodef212b2a2015-03-19 12:53:23 -0700508}
509
agoode99d63292015-04-13 08:39:25 -0700510bool MidiManagerAlsa::MidiPort::MatchNoCardPass2(const MidiPort& query) const {
511 // Matches on:
512 // connected == false
513 // type
514 // path.empty(), for both this and query
515 // id.empty(), for both this and query
516 // port_id
517 // client_name
518 // port_name
519 // midi_device == -1, for both this and query
520 return !connected() && (type() == query.type()) && path().empty() &&
521 query.path().empty() && id().empty() && query.id().empty() &&
522 (port_id() == query.port_id()) &&
523 (client_name() == query.client_name()) &&
524 (port_name() == query.port_name()) && (midi_device() == -1) &&
525 (query.midi_device() == -1);
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000526}
527
agoodeb0582872015-05-20 05:22:24 -0700528MidiManagerAlsa::MidiPortStateBase::~MidiPortStateBase() = default;
agoode99d63292015-04-13 08:39:25 -0700529
530MidiManagerAlsa::MidiPortStateBase::iterator
531MidiManagerAlsa::MidiPortStateBase::Find(
532 const MidiManagerAlsa::MidiPort& port) {
533 auto result = FindConnected(port);
534 if (result == end())
535 result = FindDisconnected(port);
536 return result;
537}
538
539MidiManagerAlsa::MidiPortStateBase::iterator
540MidiManagerAlsa::MidiPortStateBase::FindConnected(
541 const MidiManagerAlsa::MidiPort& port) {
542 // Exact match required for connected ports.
danakj75afea02016-04-25 20:36:04 -0700543 auto it = std::find_if(ports_.begin(), ports_.end(),
544 [&port](std::unique_ptr<MidiPort>& p) {
545 return p->MatchConnected(port);
546 });
agoode99d63292015-04-13 08:39:25 -0700547 return it;
548}
549
550MidiManagerAlsa::MidiPortStateBase::iterator
551MidiManagerAlsa::MidiPortStateBase::FindDisconnected(
552 const MidiManagerAlsa::MidiPort& port) {
553 // Always match on:
554 // type
555 // Possible things to match on:
556 // path
557 // id
558 // client_id
559 // port_id
560 // midi_device
561 // client_name
562 // port_name
563
564 if (!port.path().empty()) {
565 // If path is present, then we have a card-based client.
566
567 // Pass 1. Match on path, id, midi_device, port_id.
568 // This is the best possible match for hardware card-based clients.
569 // This will also match the empty id correctly for devices without an id.
danakj75afea02016-04-25 20:36:04 -0700570 auto it = std::find_if(ports_.begin(), ports_.end(),
571 [&port](std::unique_ptr<MidiPort>& p) {
572 return p->MatchCardPass1(port);
573 });
agoode99d63292015-04-13 08:39:25 -0700574 if (it != ports_.end())
575 return it;
576
577 if (!port.id().empty()) {
578 // Pass 2. Match on id, midi_device, port_id.
579 // This will give us a high-confidence match when a user moves a device to
580 // another USB/Firewire/Thunderbolt/etc port, but only works if the device
581 // has a hardware id.
danakj75afea02016-04-25 20:36:04 -0700582 it = std::find_if(ports_.begin(), ports_.end(),
583 [&port](std::unique_ptr<MidiPort>& p) {
584 return p->MatchCardPass2(port);
585 });
agoode99d63292015-04-13 08:39:25 -0700586 if (it != ports_.end())
587 return it;
588 }
589 } else {
590 // Else, we have a non-card-based client.
591 // Pass 1. Match on client_id, port_id, client_name, port_name.
592 // This will give us a reasonably good match.
danakj75afea02016-04-25 20:36:04 -0700593 auto it = std::find_if(ports_.begin(), ports_.end(),
594 [&port](std::unique_ptr<MidiPort>& p) {
595 return p->MatchNoCardPass1(port);
596 });
agoode99d63292015-04-13 08:39:25 -0700597 if (it != ports_.end())
598 return it;
599
600 // Pass 2. Match on port_id, client_name, port_name.
601 // This is weaker but similar to pass 2 in the hardware card-based clients
602 // match.
danakj75afea02016-04-25 20:36:04 -0700603 it = std::find_if(ports_.begin(), ports_.end(),
604 [&port](std::unique_ptr<MidiPort>& p) {
605 return p->MatchNoCardPass2(port);
606 });
agoode99d63292015-04-13 08:39:25 -0700607 if (it != ports_.end())
608 return it;
609 }
610
611 // No match.
612 return ports_.end();
613}
614
agoodeb0582872015-05-20 05:22:24 -0700615MidiManagerAlsa::MidiPortStateBase::MidiPortStateBase() = default;
agoode99d63292015-04-13 08:39:25 -0700616
agoodedf1b9ff2015-06-25 18:14:50 -0700617MidiManagerAlsa::MidiPortState::MidiPortState() = default;
agoode99d63292015-04-13 08:39:25 -0700618
danakj75afea02016-04-25 20:36:04 -0700619uint32_t MidiManagerAlsa::MidiPortState::push_back(
620 std::unique_ptr<MidiPort> port) {
agoode99d63292015-04-13 08:39:25 -0700621 // Add the web midi index.
Avi Drissman3528fd02015-12-18 20:11:31 -0500622 uint32_t web_port_index = 0;
agoode99d63292015-04-13 08:39:25 -0700623 switch (port->type()) {
624 case MidiPort::Type::kInput:
625 web_port_index = num_input_ports_++;
626 break;
627 case MidiPort::Type::kOutput:
628 web_port_index = num_output_ports_++;
629 break;
630 }
631 port->set_web_port_index(web_port_index);
dchengc2aeece2015-12-27 00:54:00 -0800632 MidiPortStateBase::push_back(std::move(port));
agoode99d63292015-04-13 08:39:25 -0700633 return web_port_index;
634}
635
agoodedf1b9ff2015-06-25 18:14:50 -0700636MidiManagerAlsa::AlsaSeqState::AlsaSeqState() = default;
agoode99d63292015-04-13 08:39:25 -0700637
agoodeb0582872015-05-20 05:22:24 -0700638MidiManagerAlsa::AlsaSeqState::~AlsaSeqState() = default;
agoode99d63292015-04-13 08:39:25 -0700639
640void MidiManagerAlsa::AlsaSeqState::ClientStart(int client_id,
641 const std::string& client_name,
642 snd_seq_client_type_t type) {
643 ClientExit(client_id);
ricea37e45762016-08-25 02:43:39 -0700644 clients_.insert(
645 std::make_pair(client_id, base::MakeUnique<Client>(client_name, type)));
agoodeb09423b2015-05-11 11:39:57 -0700646 if (IsCardClient(type, client_id))
647 ++card_client_count_;
agoode99d63292015-04-13 08:39:25 -0700648}
649
650bool MidiManagerAlsa::AlsaSeqState::ClientStarted(int client_id) {
651 return clients_.find(client_id) != clients_.end();
652}
653
654void MidiManagerAlsa::AlsaSeqState::ClientExit(int client_id) {
655 auto it = clients_.find(client_id);
656 if (it != clients_.end()) {
agoodeb09423b2015-05-11 11:39:57 -0700657 if (IsCardClient(it->second->type(), client_id))
658 --card_client_count_;
agoode99d63292015-04-13 08:39:25 -0700659 clients_.erase(it);
660 }
661}
662
663void MidiManagerAlsa::AlsaSeqState::PortStart(
664 int client_id,
665 int port_id,
666 const std::string& port_name,
667 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
668 bool midi) {
669 auto it = clients_.find(client_id);
670 if (it != clients_.end())
671 it->second->AddPort(port_id,
ricea37e45762016-08-25 02:43:39 -0700672 base::MakeUnique<Port>(port_name, direction, midi));
agoode99d63292015-04-13 08:39:25 -0700673}
674
675void MidiManagerAlsa::AlsaSeqState::PortExit(int client_id, int port_id) {
676 auto it = clients_.find(client_id);
677 if (it != clients_.end())
678 it->second->RemovePort(port_id);
679}
680
681snd_seq_client_type_t MidiManagerAlsa::AlsaSeqState::ClientType(
682 int client_id) const {
683 auto it = clients_.find(client_id);
684 if (it == clients_.end())
685 return SND_SEQ_USER_CLIENT;
686 return it->second->type();
687}
688
danakj75afea02016-04-25 20:36:04 -0700689std::unique_ptr<MidiManagerAlsa::TemporaryMidiPortState>
agoodeb09423b2015-05-11 11:39:57 -0700690MidiManagerAlsa::AlsaSeqState::ToMidiPortState(const AlsaCardMap& alsa_cards) {
danakj75afea02016-04-25 20:36:04 -0700691 std::unique_ptr<MidiManagerAlsa::TemporaryMidiPortState> midi_ports(
agoode99d63292015-04-13 08:39:25 -0700692 new TemporaryMidiPortState);
agoodeb09423b2015-05-11 11:39:57 -0700693 auto card_it = alsa_cards.begin();
agoode99d63292015-04-13 08:39:25 -0700694
agoodeb09423b2015-05-11 11:39:57 -0700695 int card_midi_device = -1;
agoode99d63292015-04-13 08:39:25 -0700696 for (const auto& client_pair : clients_) {
697 int client_id = client_pair.first;
vmpstr0205abb2016-06-28 18:50:56 -0700698 auto* client = client_pair.second.get();
agoode99d63292015-04-13 08:39:25 -0700699
700 // Get client metadata.
701 const std::string client_name = client->name();
702 std::string manufacturer;
703 std::string driver;
704 std::string path;
agooded87fc0f2015-05-21 08:29:31 -0700705 MidiPort::Id id;
agoode99d63292015-04-13 08:39:25 -0700706 std::string card_name;
707 std::string card_longname;
708 int midi_device = -1;
709
agoodeb09423b2015-05-11 11:39:57 -0700710 if (IsCardClient(client->type(), client_id)) {
711 auto& card = card_it->second;
712 if (card_midi_device == -1)
713 card_midi_device = 0;
714
715 manufacturer = card->manufacturer();
agooded87fc0f2015-05-21 08:29:31 -0700716 path = card->path();
717 id = MidiPort::Id(card->bus(), card->vendor_id(), card->model_id(),
718 card->usb_interface_num(), card->serial());
719 card_name = card->name();
720 card_longname = card->longname();
agoodeb09423b2015-05-11 11:39:57 -0700721 midi_device = card_midi_device;
722
723 ++card_midi_device;
724 if (card_midi_device >= card->midi_device_count()) {
725 card_midi_device = -1;
726 ++card_it;
727 }
728 }
729
agoode99d63292015-04-13 08:39:25 -0700730 for (const auto& port_pair : *client) {
731 int port_id = port_pair.first;
732 const auto& port = port_pair.second;
733
734 if (port->midi()) {
735 std::string version;
736 if (!driver.empty()) {
737 version = driver + " / ";
738 }
739 version +=
740 base::StringPrintf("ALSA library version %d.%d.%d", SND_LIB_MAJOR,
741 SND_LIB_MINOR, SND_LIB_SUBMINOR);
742 PortDirection direction = port->direction();
743 if (direction == PortDirection::kInput ||
744 direction == PortDirection::kDuplex) {
ricea37e45762016-08-25 02:43:39 -0700745 midi_ports->push_back(base::MakeUnique<MidiPort>(
agoode99d63292015-04-13 08:39:25 -0700746 path, id, client_id, port_id, midi_device, client->name(),
ricea37e45762016-08-25 02:43:39 -0700747 port->name(), manufacturer, version, MidiPort::Type::kInput));
agoode99d63292015-04-13 08:39:25 -0700748 }
749 if (direction == PortDirection::kOutput ||
750 direction == PortDirection::kDuplex) {
ricea37e45762016-08-25 02:43:39 -0700751 midi_ports->push_back(base::MakeUnique<MidiPort>(
agoode99d63292015-04-13 08:39:25 -0700752 path, id, client_id, port_id, midi_device, client->name(),
ricea37e45762016-08-25 02:43:39 -0700753 port->name(), manufacturer, version, MidiPort::Type::kOutput));
agoode99d63292015-04-13 08:39:25 -0700754 }
755 }
756 }
757 }
758
dchengc2aeece2015-12-27 00:54:00 -0800759 return midi_ports;
agoode99d63292015-04-13 08:39:25 -0700760}
761
762MidiManagerAlsa::AlsaSeqState::Port::Port(
763 const std::string& name,
764 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
765 bool midi)
766 : name_(name), direction_(direction), midi_(midi) {
767}
768
agoodeb0582872015-05-20 05:22:24 -0700769MidiManagerAlsa::AlsaSeqState::Port::~Port() = default;
agoode99d63292015-04-13 08:39:25 -0700770
771MidiManagerAlsa::AlsaSeqState::Client::Client(const std::string& name,
772 snd_seq_client_type_t type)
mgiucad9af8452015-06-25 02:11:57 -0700773 : name_(name), type_(type) {
agoode99d63292015-04-13 08:39:25 -0700774}
775
agoodeb0582872015-05-20 05:22:24 -0700776MidiManagerAlsa::AlsaSeqState::Client::~Client() = default;
agoode99d63292015-04-13 08:39:25 -0700777
danakj75afea02016-04-25 20:36:04 -0700778void MidiManagerAlsa::AlsaSeqState::Client::AddPort(
779 int addr,
780 std::unique_ptr<Port> port) {
limasdfe59d0392015-11-19 20:28:57 -0800781 ports_[addr] = std::move(port);
agoode99d63292015-04-13 08:39:25 -0700782}
783
784void MidiManagerAlsa::AlsaSeqState::Client::RemovePort(int addr) {
mgiucad9af8452015-06-25 02:11:57 -0700785 ports_.erase(addr);
agoode99d63292015-04-13 08:39:25 -0700786}
787
788MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
789MidiManagerAlsa::AlsaSeqState::Client::begin() const {
790 return ports_.begin();
791}
792
793MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
794MidiManagerAlsa::AlsaSeqState::Client::end() const {
795 return ports_.end();
agoodeaf6e9f52015-03-24 10:23:49 -0700796}
797
agoodeb09423b2015-05-11 11:39:57 -0700798MidiManagerAlsa::AlsaCard::AlsaCard(udev_device* dev,
agooded87fc0f2015-05-21 08:29:31 -0700799 const std::string& name,
800 const std::string& longname,
801 const std::string& driver,
agoodeb09423b2015-05-11 11:39:57 -0700802 int midi_device_count)
agooded87fc0f2015-05-21 08:29:31 -0700803 : name_(name),
804 longname_(longname),
805 driver_(driver),
806 path_(device::UdevDeviceGetPropertyValue(dev, kUdevIdPath)),
807 bus_(device::UdevDeviceGetPropertyValue(dev, kUdevIdBus)),
808 vendor_id_(
809 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendorId, kSysattrVendor)),
810 model_id_(
811 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdModelId, kSysattrModel)),
812 usb_interface_num_(
813 device::UdevDeviceGetPropertyValue(dev, kUdevIdUsbInterfaceNum)),
814 serial_(UdevDeviceGetPropertyOrSysattr(dev,
815 kUdevIdSerialShort,
816 kSysattrGuid)),
817 midi_device_count_(midi_device_count),
818 manufacturer_(ExtractManufacturerString(
819 GetVendor(dev),
820 vendor_id_,
821 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorFromDatabase),
822 name,
823 longname)) {
agoodeb09423b2015-05-11 11:39:57 -0700824}
825
agoodeb0582872015-05-20 05:22:24 -0700826MidiManagerAlsa::AlsaCard::~AlsaCard() = default;
agoodeb09423b2015-05-11 11:39:57 -0700827
agoode55a8b522015-03-08 12:40:17 -0700828// static
agoodeb09423b2015-05-11 11:39:57 -0700829std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
agoode5e4e9cd2015-03-09 12:34:24 -0700830 const std::string& udev_id_vendor,
agoode55a8b522015-03-08 12:40:17 -0700831 const std::string& udev_id_vendor_id,
832 const std::string& udev_id_vendor_from_database,
833 const std::string& alsa_name,
834 const std::string& alsa_longname) {
835 // Let's try to determine the manufacturer. Here is the ordered preference
836 // in extraction:
agoodef212b2a2015-03-19 12:53:23 -0700837 // 1. Vendor name from the hardware device string, from udev properties
838 // or sysattrs.
agoode5e4e9cd2015-03-09 12:34:24 -0700839 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
agoode55a8b522015-03-08 12:40:17 -0700840 // 3. Heuristic from ALSA.
841
agoodee83758c2015-03-23 22:07:54 -0700842 // Is the vendor string present and not just the vendor hex id?
843 if (!udev_id_vendor.empty() && (udev_id_vendor != udev_id_vendor_id)) {
agoode55a8b522015-03-08 12:40:17 -0700844 return udev_id_vendor;
845 }
846
847 // Is there a vendor string in the hardware database?
848 if (!udev_id_vendor_from_database.empty()) {
849 return udev_id_vendor_from_database;
850 }
851
852 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
853 // We assume that card longname is in the format of
854 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
855 // a manufacturer name here.
856 size_t at_index = alsa_longname.rfind(" at ");
agoodef212b2a2015-03-19 12:53:23 -0700857 if (at_index && at_index != std::string::npos) {
agoode55a8b522015-03-08 12:40:17 -0700858 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
agoodef212b2a2015-03-19 12:53:23 -0700859 if (name_index && name_index != std::string::npos)
agoode55a8b522015-03-08 12:40:17 -0700860 return alsa_longname.substr(0, name_index - 1);
861 }
862
863 // Failure.
864 return "";
865}
866
Avi Drissman3528fd02015-12-18 20:11:31 -0500867void MidiManagerAlsa::SendMidiData(uint32_t port_index,
868 const std::vector<uint8_t>& data) {
skyostil93e2ec22015-06-17 08:49:09 -0700869 DCHECK(send_thread_.task_runner()->BelongsToCurrentThread());
agoodebd4be9b2015-03-16 19:17:25 -0700870
agoodef212b2a2015-03-19 12:53:23 -0700871 snd_midi_event_t* encoder;
872 snd_midi_event_new(kSendBufferSize, &encoder);
agoode5a1aa112015-06-21 20:51:00 -0700873 for (const auto datum : data) {
agoodebd4be9b2015-03-16 19:17:25 -0700874 snd_seq_event_t event;
agoode5a1aa112015-06-21 20:51:00 -0700875 int result = snd_midi_event_encode_byte(encoder, datum, &event);
agoodebd4be9b2015-03-16 19:17:25 -0700876 if (result == 1) {
877 // Full event, send it.
agoode99d63292015-04-13 08:39:25 -0700878 base::AutoLock lock(out_ports_lock_);
879 auto it = out_ports_.find(port_index);
880 if (it != out_ports_.end()) {
881 snd_seq_ev_set_source(&event, it->second);
882 snd_seq_ev_set_subs(&event);
883 snd_seq_ev_set_direct(&event);
agoode5a1aa112015-06-21 20:51:00 -0700884 snd_seq_event_output_direct(out_client_.get(), &event);
agoode99d63292015-04-13 08:39:25 -0700885 }
agoodebd4be9b2015-03-16 19:17:25 -0700886 }
887 }
agoodef212b2a2015-03-19 12:53:23 -0700888 snd_midi_event_free(encoder);
agoodebd4be9b2015-03-16 19:17:25 -0700889}
890
891void MidiManagerAlsa::ScheduleEventLoop() {
fdoray8baaff02016-08-25 08:36:37 -0700892 event_thread_.task_runner()->PostTask(
agoodebd4be9b2015-03-16 19:17:25 -0700893 FROM_HERE,
894 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
895}
896
897void MidiManagerAlsa::EventLoop() {
agoode975043d2015-05-11 00:46:17 -0700898 bool loop_again = true;
agoodebd4be9b2015-03-16 19:17:25 -0700899
agoode975043d2015-05-11 00:46:17 -0700900 struct pollfd pfd[2];
agoode5a1aa112015-06-21 20:51:00 -0700901 snd_seq_poll_descriptors(in_client_.get(), &pfd[0], 1, POLLIN);
agoode975043d2015-05-11 00:46:17 -0700902 pfd[1].fd = device::udev_monitor_get_fd(udev_monitor_.get());
903 pfd[1].events = POLLIN;
agoodebd4be9b2015-03-16 19:17:25 -0700904
agoode975043d2015-05-11 00:46:17 -0700905 int err = HANDLE_EINTR(poll(pfd, arraysize(pfd), -1));
906 if (err < 0) {
brettwf7f870f2015-06-09 11:05:24 -0700907 VLOG(1) << "poll fails: " << base::safe_strerror(errno);
agoode975043d2015-05-11 00:46:17 -0700908 loop_again = false;
agoodeaf6e9f52015-03-24 10:23:49 -0700909 } else {
agoode975043d2015-05-11 00:46:17 -0700910 if (pfd[0].revents & POLLIN) {
911 // Read available incoming MIDI data.
912 int remaining;
913 double timestamp =
914 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
915 do {
916 snd_seq_event_t* event;
agoode5a1aa112015-06-21 20:51:00 -0700917 err = snd_seq_event_input(in_client_.get(), &event);
918 remaining = snd_seq_event_input_pending(in_client_.get(), 0);
agoode975043d2015-05-11 00:46:17 -0700919
920 if (err == -ENOSPC) {
921 // Handle out of space error.
922 VLOG(1) << "snd_seq_event_input detected buffer overrun";
923 // We've lost events: check another way to see if we need to shut
924 // down.
925 base::AutoLock lock(shutdown_lock_);
926 if (event_thread_shutdown_)
927 loop_again = false;
928 } else if (err == -EAGAIN) {
929 // We've read all the data.
930 } else if (err < 0) {
931 // Handle other errors.
932 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
933 // TODO(agoode): Use RecordAction() or similar to log this.
934 loop_again = false;
935 } else if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
936 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
937 // Handle announce events.
938 switch (event->type) {
939 case SND_SEQ_EVENT_PORT_START:
940 // Don't use SND_SEQ_EVENT_CLIENT_START because the
941 // client name may not be set by the time we query
942 // it. It should be set by the time ports are made.
943 ProcessClientStartEvent(event->data.addr.client);
944 ProcessPortStartEvent(event->data.addr);
945 break;
946 case SND_SEQ_EVENT_CLIENT_EXIT:
947 // Check for disconnection of our "out" client. This means "shut
948 // down".
949 if (event->data.addr.client == out_client_id_) {
950 loop_again = false;
951 remaining = 0;
952 } else
953 ProcessClientExitEvent(event->data.addr);
954 break;
955 case SND_SEQ_EVENT_PORT_EXIT:
956 ProcessPortExitEvent(event->data.addr);
957 break;
958 }
959 } else {
960 // Normal operation.
961 ProcessSingleEvent(event, timestamp);
962 }
963 } while (remaining > 0);
964 }
965 if (pfd[1].revents & POLLIN) {
966 device::ScopedUdevDevicePtr dev(
967 device::udev_monitor_receive_device(udev_monitor_.get()));
968 if (dev.get())
969 ProcessUdevEvent(dev.get());
970 else
971 VLOG(1) << "udev_monitor_receive_device fails";
972 }
agoodebd4be9b2015-03-16 19:17:25 -0700973 }
974
agoodebd4be9b2015-03-16 19:17:25 -0700975 // Do again.
agoode975043d2015-05-11 00:46:17 -0700976 if (loop_again)
977 ScheduleEventLoop();
agoodebd4be9b2015-03-16 19:17:25 -0700978}
979
980void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
981 double timestamp) {
agoode99d63292015-04-13 08:39:25 -0700982 auto source_it =
983 source_map_.find(AddrToInt(event->source.client, event->source.port));
agoodebd4be9b2015-03-16 19:17:25 -0700984 if (source_it != source_map_.end()) {
Avi Drissman3528fd02015-12-18 20:11:31 -0500985 uint32_t source = source_it->second;
agoodebd4be9b2015-03-16 19:17:25 -0700986 if (event->type == SND_SEQ_EVENT_SYSEX) {
987 // Special! Variable-length sysex.
Avi Drissman3528fd02015-12-18 20:11:31 -0500988 ReceiveMidiData(source, static_cast<const uint8_t*>(event->data.ext.ptr),
agoodebd4be9b2015-03-16 19:17:25 -0700989 event->data.ext.len, timestamp);
990 } else {
991 // Otherwise, decode this and send that on.
992 unsigned char buf[12];
agoode5a1aa112015-06-21 20:51:00 -0700993 long count =
994 snd_midi_event_decode(decoder_.get(), buf, sizeof(buf), event);
agoodebd4be9b2015-03-16 19:17:25 -0700995 if (count <= 0) {
996 if (count != -ENOENT) {
997 // ENOENT means that it's not a MIDI message, which is not an
998 // error, but other negative values are errors for us.
999 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
1000 // TODO(agoode): Record this failure.
1001 }
1002 } else {
1003 ReceiveMidiData(source, buf, count, timestamp);
1004 }
1005 }
1006 }
1007}
1008
agoode99d63292015-04-13 08:39:25 -07001009void MidiManagerAlsa::ProcessClientStartEvent(int client_id) {
1010 // Ignore if client is already started.
1011 if (alsa_seq_state_.ClientStarted(client_id))
1012 return;
1013
1014 snd_seq_client_info_t* client_info;
1015 snd_seq_client_info_alloca(&client_info);
agoode5a1aa112015-06-21 20:51:00 -07001016 int err =
1017 snd_seq_get_any_client_info(in_client_.get(), client_id, client_info);
agoode99d63292015-04-13 08:39:25 -07001018 if (err != 0)
1019 return;
1020
1021 // Skip our own clients.
1022 if ((client_id == in_client_id_) || (client_id == out_client_id_))
1023 return;
1024
1025 // Update our view of ALSA seq state.
1026 alsa_seq_state_.ClientStart(client_id,
1027 snd_seq_client_info_get_name(client_info),
1028 snd_seq_client_info_get_type(client_info));
1029
1030 // Generate Web MIDI events.
1031 UpdatePortStateAndGenerateEvents();
1032}
1033
1034void MidiManagerAlsa::ProcessPortStartEvent(const snd_seq_addr_t& addr) {
1035 snd_seq_port_info_t* port_info;
1036 snd_seq_port_info_alloca(&port_info);
agoode5a1aa112015-06-21 20:51:00 -07001037 int err = snd_seq_get_any_port_info(in_client_.get(), addr.client, addr.port,
1038 port_info);
agoode99d63292015-04-13 08:39:25 -07001039 if (err != 0)
1040 return;
1041
1042 unsigned int caps = snd_seq_port_info_get_capability(port_info);
1043 bool input = (caps & kRequiredInputPortCaps) == kRequiredInputPortCaps;
1044 bool output = (caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps;
1045 AlsaSeqState::PortDirection direction;
1046 if (input && output)
1047 direction = AlsaSeqState::PortDirection::kDuplex;
1048 else if (input)
1049 direction = AlsaSeqState::PortDirection::kInput;
1050 else if (output)
1051 direction = AlsaSeqState::PortDirection::kOutput;
1052 else
1053 return;
1054
1055 // Update our view of ALSA seq state.
1056 alsa_seq_state_.PortStart(
1057 addr.client, addr.port, snd_seq_port_info_get_name(port_info), direction,
1058 snd_seq_port_info_get_type(port_info) & SND_SEQ_PORT_TYPE_MIDI_GENERIC);
1059 // Generate Web MIDI events.
1060 UpdatePortStateAndGenerateEvents();
1061}
1062
1063void MidiManagerAlsa::ProcessClientExitEvent(const snd_seq_addr_t& addr) {
1064 // Update our view of ALSA seq state.
1065 alsa_seq_state_.ClientExit(addr.client);
1066 // Generate Web MIDI events.
1067 UpdatePortStateAndGenerateEvents();
1068}
1069
1070void MidiManagerAlsa::ProcessPortExitEvent(const snd_seq_addr_t& addr) {
1071 // Update our view of ALSA seq state.
1072 alsa_seq_state_.PortExit(addr.client, addr.port);
1073 // Generate Web MIDI events.
1074 UpdatePortStateAndGenerateEvents();
1075}
1076
agoode975043d2015-05-11 00:46:17 -07001077void MidiManagerAlsa::ProcessUdevEvent(udev_device* dev) {
1078 // Only card devices have this property set, and only when they are
1079 // fully initialized.
1080 if (!device::udev_device_get_property_value(dev,
1081 kUdevPropertySoundInitialized))
1082 return;
1083
1084 // Get the action. If no action, then we are doing first time enumeration
1085 // and the device is treated as new.
1086 const char* action = device::udev_device_get_action(dev);
1087 if (!action)
1088 action = kUdevActionChange;
1089
1090 if (strcmp(action, kUdevActionChange) == 0) {
agoodeb09423b2015-05-11 11:39:57 -07001091 AddCard(dev);
1092 // Generate Web MIDI events.
1093 UpdatePortStateAndGenerateEvents();
agoode975043d2015-05-11 00:46:17 -07001094 } else if (strcmp(action, kUdevActionRemove) == 0) {
agoodeb09423b2015-05-11 11:39:57 -07001095 RemoveCard(GetCardNumber(dev));
1096 // Generate Web MIDI events.
1097 UpdatePortStateAndGenerateEvents();
agoode975043d2015-05-11 00:46:17 -07001098 }
1099}
1100
agoodeb09423b2015-05-11 11:39:57 -07001101void MidiManagerAlsa::AddCard(udev_device* dev) {
1102 int number = GetCardNumber(dev);
1103 if (number == -1)
1104 return;
1105
1106 RemoveCard(number);
1107
1108 snd_ctl_card_info_t* card;
1109 snd_hwdep_info_t* hwdep;
1110 snd_ctl_card_info_alloca(&card);
1111 snd_hwdep_info_alloca(&hwdep);
1112 const std::string id = base::StringPrintf("hw:CARD=%i", number);
1113 snd_ctl_t* handle;
1114 int err = snd_ctl_open(&handle, id.c_str(), 0);
1115 if (err != 0) {
1116 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
1117 return;
1118 }
1119 err = snd_ctl_card_info(handle, card);
1120 if (err != 0) {
1121 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
1122 snd_ctl_close(handle);
1123 return;
1124 }
1125 std::string name = snd_ctl_card_info_get_name(card);
1126 std::string longname = snd_ctl_card_info_get_longname(card);
1127 std::string driver = snd_ctl_card_info_get_driver(card);
1128
1129 // Count rawmidi devices (not subdevices).
1130 int midi_count = 0;
1131 for (int device = -1;
1132 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;)
1133 ++midi_count;
1134
1135 // Count any hwdep synths that become MIDI devices outside of rawmidi.
1136 //
1137 // Explanation:
1138 // Any kernel driver can create an ALSA client (visible to us).
1139 // With modern hardware, only rawmidi devices do this. Kernel
1140 // drivers create rawmidi devices and the rawmidi subsystem makes
1141 // the seq clients. But the OPL3 driver is special, it does not
1142 // make a rawmidi device but a seq client directly. (This is the
1143 // only one to worry about in the kernel code, as of 2015-03-23.)
1144 //
1145 // OPL3 is very old (but still possible to get in new
1146 // hardware). It is unlikely that new drivers would not use
1147 // rawmidi and defeat our heuristic.
1148 //
1149 // Longer term, support should be added in the kernel to expose a
1150 // direct link from card->client (or client->card) so that all
1151 // these heuristics will be obsolete. Once that is there, we can
1152 // assume our old heuristics will work on old kernels and the new
1153 // robust code will be used on new. Then we will not need to worry
1154 // about changes to kernel internals breaking our code.
1155 // See the TODO above at kMinimumClientIdForCards.
1156 for (int device = -1;
1157 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) {
1158 err = snd_ctl_hwdep_info(handle, hwdep);
1159 if (err != 0) {
1160 VLOG(1) << "snd_ctl_hwdep_info fails: " << snd_strerror(err);
1161 continue;
1162 }
1163 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep);
1164 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 ||
1165 iface == SND_HWDEP_IFACE_OPL4)
1166 ++midi_count;
1167 }
1168 snd_ctl_close(handle);
1169
mgiucad9af8452015-06-25 02:11:57 -07001170 if (midi_count > 0) {
danakj75afea02016-04-25 20:36:04 -07001171 std::unique_ptr<AlsaCard> card(
mgiucad9af8452015-06-25 02:11:57 -07001172 new AlsaCard(dev, name, longname, driver, midi_count));
limasdfe59d0392015-11-19 20:28:57 -08001173 alsa_cards_.insert(std::make_pair(number, std::move(card)));
mgiucad9af8452015-06-25 02:11:57 -07001174 alsa_card_midi_count_ += midi_count;
1175 }
agoodeb09423b2015-05-11 11:39:57 -07001176}
1177
1178void MidiManagerAlsa::RemoveCard(int number) {
1179 auto it = alsa_cards_.find(number);
1180 if (it == alsa_cards_.end())
1181 return;
1182
1183 alsa_card_midi_count_ -= it->second->midi_device_count();
agoodeb09423b2015-05-11 11:39:57 -07001184 alsa_cards_.erase(it);
1185}
1186
agoode99d63292015-04-13 08:39:25 -07001187void MidiManagerAlsa::UpdatePortStateAndGenerateEvents() {
agoodeb09423b2015-05-11 11:39:57 -07001188 // Verify that our information from ALSA and udev are in sync. If
1189 // not, we cannot generate events right now.
1190 if (alsa_card_midi_count_ != alsa_seq_state_.card_client_count())
1191 return;
1192
agoode99d63292015-04-13 08:39:25 -07001193 // Generate new port state.
agoodeb09423b2015-05-11 11:39:57 -07001194 auto new_port_state = alsa_seq_state_.ToMidiPortState(alsa_cards_);
agoode99d63292015-04-13 08:39:25 -07001195
1196 // Disconnect any connected old ports that are now missing.
agoode5ebc4932015-12-01 08:25:12 -08001197 for (auto& old_port : port_state_) {
agoode99d63292015-04-13 08:39:25 -07001198 if (old_port->connected() &&
1199 (new_port_state->FindConnected(*old_port) == new_port_state->end())) {
1200 old_port->set_connected(false);
Avi Drissman3528fd02015-12-18 20:11:31 -05001201 uint32_t web_port_index = old_port->web_port_index();
agoode99d63292015-04-13 08:39:25 -07001202 switch (old_port->type()) {
1203 case MidiPort::Type::kInput:
1204 source_map_.erase(
1205 AddrToInt(old_port->client_id(), old_port->port_id()));
toyoshimec2570a2016-10-21 02:15:27 -07001206 SetInputPortState(web_port_index, PortState::DISCONNECTED);
agoode99d63292015-04-13 08:39:25 -07001207 break;
1208 case MidiPort::Type::kOutput:
1209 DeleteAlsaOutputPort(web_port_index);
toyoshimec2570a2016-10-21 02:15:27 -07001210 SetOutputPortState(web_port_index, PortState::DISCONNECTED);
agoode99d63292015-04-13 08:39:25 -07001211 break;
1212 }
1213 }
1214 }
1215
1216 // Reconnect or add new ports.
1217 auto it = new_port_state->begin();
1218 while (it != new_port_state->end()) {
agoode5ebc4932015-12-01 08:25:12 -08001219 auto& new_port = *it;
agoode99d63292015-04-13 08:39:25 -07001220 auto old_port = port_state_.Find(*new_port);
1221 if (old_port == port_state_.end()) {
1222 // Add new port.
agoode5ebc4932015-12-01 08:25:12 -08001223 const auto& opaque_key = new_port->OpaqueKey();
1224 const auto& manufacturer = new_port->manufacturer();
1225 const auto& port_name = new_port->port_name();
1226 const auto& version = new_port->version();
1227 const auto& type = new_port->type();
1228 const auto& client_id = new_port->client_id();
1229 const auto& port_id = new_port->port_id();
1230
dchengc2aeece2015-12-27 00:54:00 -08001231 uint32_t web_port_index = port_state_.push_back(std::move(new_port));
agoode5ebc4932015-12-01 08:25:12 -08001232 it = new_port_state->erase(it);
1233
1234 MidiPortInfo info(opaque_key, manufacturer, port_name, version,
toyoshimec2570a2016-10-21 02:15:27 -07001235 PortState::OPENED);
agoode5ebc4932015-12-01 08:25:12 -08001236 switch (type) {
agoode99d63292015-04-13 08:39:25 -07001237 case MidiPort::Type::kInput:
agoode5ebc4932015-12-01 08:25:12 -08001238 if (Subscribe(web_port_index, client_id, port_id))
agoode99d63292015-04-13 08:39:25 -07001239 AddInputPort(info);
1240 break;
agoode99d63292015-04-13 08:39:25 -07001241 case MidiPort::Type::kOutput:
agoode5ebc4932015-12-01 08:25:12 -08001242 if (CreateAlsaOutputPort(web_port_index, client_id, port_id))
agoode99d63292015-04-13 08:39:25 -07001243 AddOutputPort(info);
1244 break;
1245 }
agoode99d63292015-04-13 08:39:25 -07001246 } else if (!(*old_port)->connected()) {
1247 // Reconnect.
Avi Drissman3528fd02015-12-18 20:11:31 -05001248 uint32_t web_port_index = (*old_port)->web_port_index();
agoode99d63292015-04-13 08:39:25 -07001249 (*old_port)->Update(new_port->path(), new_port->client_id(),
1250 new_port->port_id(), new_port->client_name(),
1251 new_port->port_name(), new_port->manufacturer(),
1252 new_port->version());
1253 switch ((*old_port)->type()) {
1254 case MidiPort::Type::kInput:
1255 if (Subscribe(web_port_index, (*old_port)->client_id(),
1256 (*old_port)->port_id()))
toyoshimec2570a2016-10-21 02:15:27 -07001257 SetInputPortState(web_port_index, PortState::OPENED);
agoode99d63292015-04-13 08:39:25 -07001258 break;
agoode99d63292015-04-13 08:39:25 -07001259 case MidiPort::Type::kOutput:
1260 if (CreateAlsaOutputPort(web_port_index, (*old_port)->client_id(),
1261 (*old_port)->port_id()))
toyoshimec2570a2016-10-21 02:15:27 -07001262 SetOutputPortState(web_port_index, PortState::OPENED);
agoode99d63292015-04-13 08:39:25 -07001263 break;
1264 }
1265 (*old_port)->set_connected(true);
1266 ++it;
1267 } else {
1268 ++it;
1269 }
1270 }
1271}
1272
agoode975043d2015-05-11 00:46:17 -07001273// TODO(agoode): return false on failure.
agoode99d63292015-04-13 08:39:25 -07001274void MidiManagerAlsa::EnumerateAlsaPorts() {
1275 snd_seq_client_info_t* client_info;
1276 snd_seq_client_info_alloca(&client_info);
1277 snd_seq_port_info_t* port_info;
1278 snd_seq_port_info_alloca(&port_info);
1279
1280 // Enumerate clients.
1281 snd_seq_client_info_set_client(client_info, -1);
agoode5a1aa112015-06-21 20:51:00 -07001282 while (!snd_seq_query_next_client(in_client_.get(), client_info)) {
agoode99d63292015-04-13 08:39:25 -07001283 int client_id = snd_seq_client_info_get_client(client_info);
1284 ProcessClientStartEvent(client_id);
1285
1286 // Enumerate ports.
1287 snd_seq_port_info_set_client(port_info, client_id);
1288 snd_seq_port_info_set_port(port_info, -1);
agoode5a1aa112015-06-21 20:51:00 -07001289 while (!snd_seq_query_next_port(in_client_.get(), port_info)) {
agoode99d63292015-04-13 08:39:25 -07001290 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
1291 ProcessPortStartEvent(*addr);
1292 }
1293 }
1294}
1295
agoode975043d2015-05-11 00:46:17 -07001296bool MidiManagerAlsa::EnumerateUdevCards() {
1297 int err;
1298
1299 device::ScopedUdevEnumeratePtr enumerate(
1300 device::udev_enumerate_new(udev_.get()));
1301 if (!enumerate.get()) {
1302 VLOG(1) << "udev_enumerate_new fails";
1303 return false;
1304 }
1305
1306 err = device::udev_enumerate_add_match_subsystem(enumerate.get(),
1307 kUdevSubsystemSound);
1308 if (err) {
1309 VLOG(1) << "udev_enumerate_add_match_subsystem fails: "
brettwf7f870f2015-06-09 11:05:24 -07001310 << base::safe_strerror(-err);
agoode975043d2015-05-11 00:46:17 -07001311 return false;
1312 }
1313
1314 err = device::udev_enumerate_scan_devices(enumerate.get());
1315 if (err) {
brettwf7f870f2015-06-09 11:05:24 -07001316 VLOG(1) << "udev_enumerate_scan_devices fails: "
1317 << base::safe_strerror(-err);
agoode975043d2015-05-11 00:46:17 -07001318 return false;
1319 }
1320
1321 udev_list_entry* list_entry;
1322 auto* devices = device::udev_enumerate_get_list_entry(enumerate.get());
1323 udev_list_entry_foreach(list_entry, devices) {
1324 const char* path = device::udev_list_entry_get_name(list_entry);
1325 device::ScopedUdevDevicePtr dev(
1326 device::udev_device_new_from_syspath(udev_.get(), path));
1327 if (dev.get())
1328 ProcessUdevEvent(dev.get());
1329 }
1330
1331 return true;
1332}
1333
Avi Drissman3528fd02015-12-18 20:11:31 -05001334bool MidiManagerAlsa::CreateAlsaOutputPort(uint32_t port_index,
agoode99d63292015-04-13 08:39:25 -07001335 int client_id,
1336 int port_id) {
1337 // Create the port.
1338 int out_port = snd_seq_create_simple_port(
agoode5a1aa112015-06-21 20:51:00 -07001339 out_client_.get(), NULL, kCreateOutputPortCaps, kCreatePortType);
agoode99d63292015-04-13 08:39:25 -07001340 if (out_port < 0) {
1341 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(out_port);
1342 return false;
1343 }
1344 // Activate port subscription.
1345 snd_seq_port_subscribe_t* subs;
1346 snd_seq_port_subscribe_alloca(&subs);
1347 snd_seq_addr_t sender;
1348 sender.client = out_client_id_;
1349 sender.port = out_port;
1350 snd_seq_port_subscribe_set_sender(subs, &sender);
1351 snd_seq_addr_t dest;
1352 dest.client = client_id;
1353 dest.port = port_id;
1354 snd_seq_port_subscribe_set_dest(subs, &dest);
agoode5a1aa112015-06-21 20:51:00 -07001355 int err = snd_seq_subscribe_port(out_client_.get(), subs);
agoode99d63292015-04-13 08:39:25 -07001356 if (err != 0) {
1357 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
agoode5a1aa112015-06-21 20:51:00 -07001358 snd_seq_delete_simple_port(out_client_.get(), out_port);
agoode99d63292015-04-13 08:39:25 -07001359 return false;
1360 }
1361
1362 // Update our map.
1363 base::AutoLock lock(out_ports_lock_);
1364 out_ports_[port_index] = out_port;
1365 return true;
1366}
1367
Avi Drissman3528fd02015-12-18 20:11:31 -05001368void MidiManagerAlsa::DeleteAlsaOutputPort(uint32_t port_index) {
agoode99d63292015-04-13 08:39:25 -07001369 base::AutoLock lock(out_ports_lock_);
1370 auto it = out_ports_.find(port_index);
1371 if (it == out_ports_.end())
1372 return;
1373
1374 int alsa_port = it->second;
agoode5a1aa112015-06-21 20:51:00 -07001375 snd_seq_delete_simple_port(out_client_.get(), alsa_port);
agoode99d63292015-04-13 08:39:25 -07001376 out_ports_.erase(it);
1377}
1378
Avi Drissman3528fd02015-12-18 20:11:31 -05001379bool MidiManagerAlsa::Subscribe(uint32_t port_index,
1380 int client_id,
1381 int port_id) {
agoode99d63292015-04-13 08:39:25 -07001382 // Activate port subscription.
1383 snd_seq_port_subscribe_t* subs;
1384 snd_seq_port_subscribe_alloca(&subs);
1385 snd_seq_addr_t sender;
1386 sender.client = client_id;
1387 sender.port = port_id;
1388 snd_seq_port_subscribe_set_sender(subs, &sender);
1389 snd_seq_addr_t dest;
1390 dest.client = in_client_id_;
1391 dest.port = in_port_id_;
1392 snd_seq_port_subscribe_set_dest(subs, &dest);
agoode5a1aa112015-06-21 20:51:00 -07001393 int err = snd_seq_subscribe_port(in_client_.get(), subs);
agoode99d63292015-04-13 08:39:25 -07001394 if (err != 0) {
1395 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
1396 return false;
1397 }
1398
1399 // Update our map.
1400 source_map_[AddrToInt(client_id, port_id)] = port_index;
1401 return true;
1402}
1403
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +00001404MidiManager* MidiManager::Create() {
1405 return new MidiManagerAlsa();
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +00001406}
1407
toyoshime147c5e2015-05-07 21:58:31 -07001408} // namespace midi