blob: 05efb49be60fd66817a44cd2e928d6646ef53dd6 [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"
toyoshimf4d61522017-02-10 02:03:32 -080018#include "base/lazy_instance.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000019#include "base/logging.h"
avi793390d2015-12-22 22:22:36 -080020#include "base/macros.h"
danakj75afea02016-04-25 20:36:04 -070021#include "base/memory/ptr_util.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000022#include "base/message_loop/message_loop.h"
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000023#include "base/posix/eintr_wrapper.h"
brettwf7f870f2015-06-09 11:05:24 -070024#include "base/posix/safe_strerror.h"
fdoray8baaff02016-08-25 08:36:37 -070025#include "base/single_thread_task_runner.h"
agoodef212b2a2015-03-19 12:53:23 -070026#include "base/strings/string_number_conversions.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000027#include "base/strings/stringprintf.h"
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000028#include "base/time/time.h"
agoodef212b2a2015-03-19 12:53:23 -070029#include "crypto/sha2.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000030#include "media/midi/midi_port_info.h"
toyoshimf4d61522017-02-10 02:03:32 -080031#include "media/midi/midi_service.h"
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000032
toyoshime147c5e2015-05-07 21:58:31 -070033namespace midi {
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +000034
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +000035namespace {
36
toyoshimec2570a2016-10-21 02:15:27 -070037using mojom::PortState;
38using mojom::Result;
toyoshim2f3a48f2016-10-17 01:54:13 -070039
toyoshimf4d61522017-02-10 02:03:32 -080040// TODO(toyoshim): use constexpr for following const values.
41const int kEventTaskRunner = 0;
42const int kSendTaskRunner = 1;
43
agoode@chromium.org25227512014-06-08 05:12:05 +000044// Per-output buffer. This can be smaller, but then large sysex messages
45// will be (harmlessly) split across multiple seq events. This should
46// not have any real practical effect, except perhaps to slightly reorder
47// realtime messages with respect to sysex.
48const size_t kSendBufferSize = 256;
49
agoodeb09423b2015-05-11 11:39:57 -070050// Minimum client id for which we will have ALSA card devices for. When we
51// are searching for card devices (used to get the path, id, and manufacturer),
52// we don't want to get confused by kernel clients that do not have a card.
53// See seq_clientmgr.c in the ALSA code for this.
54// TODO(agoode): Add proper client -> card export from the kernel to avoid
55// hardcoding.
56const int kMinimumClientIdForCards = 16;
57
agoodef212b2a2015-03-19 12:53:23 -070058// ALSA constants.
59const char kAlsaHw[] = "hw";
60
agoode975043d2015-05-11 00:46:17 -070061// udev constants.
62const char kUdev[] = "udev";
63const char kUdevSubsystemSound[] = "sound";
64const char kUdevPropertySoundInitialized[] = "SOUND_INITIALIZED";
65const char kUdevActionChange[] = "change";
66const char kUdevActionRemove[] = "remove";
67
agoodeb09423b2015-05-11 11:39:57 -070068const char kUdevIdVendor[] = "ID_VENDOR";
69const char kUdevIdVendorEnc[] = "ID_VENDOR_ENC";
70const char kUdevIdVendorFromDatabase[] = "ID_VENDOR_FROM_DATABASE";
71const char kUdevIdVendorId[] = "ID_VENDOR_ID";
72const char kUdevIdModelId[] = "ID_MODEL_ID";
73const char kUdevIdBus[] = "ID_BUS";
74const char kUdevIdPath[] = "ID_PATH";
75const char kUdevIdUsbInterfaceNum[] = "ID_USB_INTERFACE_NUM";
76const char kUdevIdSerialShort[] = "ID_SERIAL_SHORT";
77
78const char kSysattrVendorName[] = "vendor_name";
79const char kSysattrVendor[] = "vendor";
80const char kSysattrModel[] = "model";
81const char kSysattrGuid[] = "guid";
82
83const char kCardSyspath[] = "/card";
84
agoode@chromium.org25227512014-06-08 05:12:05 +000085// Constants for the capabilities we search for in inputs and outputs.
86// See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
87const unsigned int kRequiredInputPortCaps =
88 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
89const unsigned int kRequiredOutputPortCaps =
90 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
91
agoode99d63292015-04-13 08:39:25 -070092const unsigned int kCreateOutputPortCaps =
93 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT;
94const unsigned int kCreateInputPortCaps =
95 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT;
96const unsigned int kCreatePortType =
97 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
agoode@chromium.org25227512014-06-08 05:12:05 +000098
toyoshimf4d61522017-02-10 02:03:32 -080099// Global variables to identify MidiManagerAlsa instance.
100const int kInvalidInstanceId = -1;
101int g_active_instance_id = kInvalidInstanceId;
102int g_next_instance_id = 0;
103base::LazyInstance<base::Lock> g_instance_id_lock = LAZY_INSTANCE_INITIALIZER;
104
105// Prevent current instance from quiting Finalize() while tasks run on external
106// TaskRunners.
107base::LazyInstance<base::Lock> g_event_task_lock = LAZY_INSTANCE_INITIALIZER;
108base::LazyInstance<base::Lock> g_send_task_lock = LAZY_INSTANCE_INITIALIZER;
109
agoode99d63292015-04-13 08:39:25 -0700110int AddrToInt(int client, int port) {
111 return (client << 8) | port;
agoodef212b2a2015-03-19 12:53:23 -0700112}
agoodef212b2a2015-03-19 12:53:23 -0700113
agoodeb09423b2015-05-11 11:39:57 -0700114// Returns true if this client has an ALSA card associated with it.
115bool IsCardClient(snd_seq_client_type_t type, int client_id) {
116 return (type == SND_SEQ_KERNEL_CLIENT) &&
117 (client_id >= kMinimumClientIdForCards);
118}
119
120// TODO(agoode): Move this to device/udev_linux.
121const std::string UdevDeviceGetPropertyOrSysattr(
122 struct udev_device* udev_device,
123 const char* property_key,
124 const char* sysattr_key) {
125 // First try the property.
126 std::string value =
127 device::UdevDeviceGetPropertyValue(udev_device, property_key);
128
129 // If no property, look for sysattrs and walk up the parent devices too.
130 while (value.empty() && udev_device) {
131 value = device::UdevDeviceGetSysattrValue(udev_device, sysattr_key);
132 udev_device = device::udev_device_get_parent(udev_device);
133 }
134 return value;
135}
136
137int GetCardNumber(udev_device* dev) {
138 const char* syspath = device::udev_device_get_syspath(dev);
139 if (!syspath)
140 return -1;
141
142 std::string syspath_str(syspath);
143 size_t i = syspath_str.rfind(kCardSyspath);
144 if (i == std::string::npos)
145 return -1;
146
147 int number;
148 if (!base::StringToInt(syspath_str.substr(i + strlen(kCardSyspath)), &number))
149 return -1;
150 return number;
151}
152
agooded87fc0f2015-05-21 08:29:31 -0700153std::string GetVendor(udev_device* dev) {
154 // Try to get the vendor string. Sometimes it is encoded.
155 std::string vendor = device::UdevDecodeString(
156 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorEnc));
157 // Sometimes it is not encoded.
158 if (vendor.empty())
159 vendor =
160 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendor, kSysattrVendorName);
161 return vendor;
162}
163
agoode8caab0b2015-03-23 18:48:02 -0700164void SetStringIfNonEmpty(base::DictionaryValue* value,
165 const std::string& path,
166 const std::string& in_value) {
167 if (!in_value.empty())
168 value->SetString(path, in_value);
169}
170
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000171} // namespace
172
toyoshimf4d61522017-02-10 02:03:32 -0800173MidiManagerAlsa::MidiManagerAlsa(MidiService* service) : MidiManager(service) {}
agoodeb2ead822016-03-11 12:14:35 -0800174
175MidiManagerAlsa::~MidiManagerAlsa() {
176 // Take lock to ensure that the members initialized on the IO thread
177 // are not destructed here.
178 base::AutoLock lock(lazy_init_member_lock_);
179
toyoshim131beb52016-07-25 00:42:41 -0700180 // Extra CHECK to verify all members are already reset.
181 CHECK(!initialization_thread_checker_);
182 CHECK(!in_client_);
183 CHECK(!out_client_);
184 CHECK(!decoder_);
185 CHECK(!udev_);
186 CHECK(!udev_monitor_);
187
toyoshimf4d61522017-02-10 02:03:32 -0800188 base::AutoLock instance_id_lock(g_instance_id_lock.Get());
189 CHECK_EQ(kInvalidInstanceId, g_active_instance_id);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000190}
191
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000192void MidiManagerAlsa::StartInitialization() {
toyoshimf4d61522017-02-10 02:03:32 -0800193 {
194 base::AutoLock lock(g_instance_id_lock.Get());
195 CHECK_EQ(kInvalidInstanceId, g_active_instance_id);
196 instance_id_ = g_next_instance_id++;
197 g_active_instance_id = instance_id_;
198 }
199
agoodeb2ead822016-03-11 12:14:35 -0800200 base::AutoLock lock(lazy_init_member_lock_);
agoode5a1aa112015-06-21 20:51:00 -0700201
agoodeb2ead822016-03-11 12:14:35 -0800202 initialization_thread_checker_.reset(new base::ThreadChecker());
203
204 // Create client handles.
205 snd_seq_t* tmp_seq = nullptr;
206 int err =
207 snd_seq_open(&tmp_seq, kAlsaHw, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
agoode@chromium.org25227512014-06-08 05:12:05 +0000208 if (err != 0) {
209 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700210 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000211 }
agoodeb2ead822016-03-11 12:14:35 -0800212 ScopedSndSeqPtr in_client(tmp_seq);
213 tmp_seq = nullptr;
214 in_client_id_ = snd_seq_client_id(in_client.get());
215
216 err = snd_seq_open(&tmp_seq, kAlsaHw, SND_SEQ_OPEN_OUTPUT, 0);
217 if (err != 0) {
218 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
219 return CompleteInitialization(Result::INITIALIZATION_ERROR);
220 }
221 ScopedSndSeqPtr out_client(tmp_seq);
222 tmp_seq = nullptr;
223 out_client_id_ = snd_seq_client_id(out_client.get());
agoode@chromium.org25227512014-06-08 05:12:05 +0000224
225 // Name the clients.
agoodeb2ead822016-03-11 12:14:35 -0800226 err = snd_seq_set_client_name(in_client.get(), "Chrome (input)");
agoode@chromium.org25227512014-06-08 05:12:05 +0000227 if (err != 0) {
228 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700229 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000230 }
agoodeb2ead822016-03-11 12:14:35 -0800231 err = snd_seq_set_client_name(out_client.get(), "Chrome (output)");
agoode@chromium.org25227512014-06-08 05:12:05 +0000232 if (err != 0) {
233 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700234 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000235 }
236
237 // Create input port.
agoode99d63292015-04-13 08:39:25 -0700238 in_port_id_ = snd_seq_create_simple_port(
agoodeb2ead822016-03-11 12:14:35 -0800239 in_client.get(), NULL, kCreateInputPortCaps, kCreatePortType);
agoode99d63292015-04-13 08:39:25 -0700240 if (in_port_id_ < 0) {
241 VLOG(1) << "snd_seq_create_simple_port fails: "
242 << snd_strerror(in_port_id_);
toyoshimf1b88962015-07-09 14:14:51 -0700243 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000244 }
245
246 // Subscribe to the announce port.
247 snd_seq_port_subscribe_t* subs;
248 snd_seq_port_subscribe_alloca(&subs);
249 snd_seq_addr_t announce_sender;
250 snd_seq_addr_t announce_dest;
251 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
252 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
agoode99d63292015-04-13 08:39:25 -0700253 announce_dest.client = in_client_id_;
254 announce_dest.port = in_port_id_;
agoode@chromium.org25227512014-06-08 05:12:05 +0000255 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
256 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
agoodeb2ead822016-03-11 12:14:35 -0800257 err = snd_seq_subscribe_port(in_client.get(), subs);
agoode@chromium.org25227512014-06-08 05:12:05 +0000258 if (err != 0) {
259 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
260 << snd_strerror(err);
toyoshimf1b88962015-07-09 14:14:51 -0700261 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode@chromium.org25227512014-06-08 05:12:05 +0000262 }
263
agoodeb2ead822016-03-11 12:14:35 -0800264 // Initialize decoder.
265 snd_midi_event_t* tmp_decoder = nullptr;
266 snd_midi_event_new(0, &tmp_decoder);
267 ScopedSndMidiEventPtr decoder(tmp_decoder);
268 tmp_decoder = nullptr;
269 snd_midi_event_no_status(decoder.get(), 1);
agoode@chromium.org25227512014-06-08 05:12:05 +0000270
agoodeb2ead822016-03-11 12:14:35 -0800271 // Initialize udev and monitor.
272 device::ScopedUdevPtr udev(device::udev_new());
273 device::ScopedUdevMonitorPtr udev_monitor(
274 device::udev_monitor_new_from_netlink(udev.get(), kUdev));
275 if (!udev_monitor.get()) {
agoode975043d2015-05-11 00:46:17 -0700276 VLOG(1) << "udev_monitor_new_from_netlink fails";
toyoshimf1b88962015-07-09 14:14:51 -0700277 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700278 }
279 err = device::udev_monitor_filter_add_match_subsystem_devtype(
agoodeb2ead822016-03-11 12:14:35 -0800280 udev_monitor.get(), kUdevSubsystemSound, nullptr);
agoode975043d2015-05-11 00:46:17 -0700281 if (err != 0) {
282 VLOG(1) << "udev_monitor_add_match_subsystem fails: "
brettwf7f870f2015-06-09 11:05:24 -0700283 << base::safe_strerror(-err);
toyoshimf1b88962015-07-09 14:14:51 -0700284 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700285 }
agoodeb2ead822016-03-11 12:14:35 -0800286 err = device::udev_monitor_enable_receiving(udev_monitor.get());
agoode975043d2015-05-11 00:46:17 -0700287 if (err != 0) {
brettwf7f870f2015-06-09 11:05:24 -0700288 VLOG(1) << "udev_monitor_enable_receiving fails: "
289 << base::safe_strerror(-err);
toyoshimf1b88962015-07-09 14:14:51 -0700290 return CompleteInitialization(Result::INITIALIZATION_ERROR);
agoode975043d2015-05-11 00:46:17 -0700291 }
292
agoodeb2ead822016-03-11 12:14:35 -0800293 // Success! Now, initialize members from the temporaries. Do not
294 // initialize these earlier, since they need to be destroyed by the
295 // thread that calls Finalize(), not the destructor thread (and we
296 // check this in the destructor).
297 in_client_.reset(in_client.release());
298 out_client_.reset(out_client.release());
299 decoder_.reset(decoder.release());
300 udev_.reset(udev.release());
agoode740f5222016-07-06 23:46:32 -0700301 udev_monitor_.reset(udev_monitor.release());
agoodeb2ead822016-03-11 12:14:35 -0800302
303 // Generate hotplug events for existing ports.
304 // TODO(agoode): Check the return value for failure.
305 EnumerateAlsaPorts();
306
307 // Generate hotplug events for existing udev devices. This must be done
308 // after udev_monitor_enable_receiving() is called. See the algorithm
309 // at http://www.signal11.us/oss/udev/.
agoode975043d2015-05-11 00:46:17 -0700310 EnumerateUdevCards();
311
agoodeb2ead822016-03-11 12:14:35 -0800312 // Start processing events. Don't do this before enumeration of both
313 // ALSA and udev.
toyoshimf4d61522017-02-10 02:03:32 -0800314 service()
315 ->GetTaskRunner(kEventTaskRunner)
316 ->PostTask(FROM_HERE, base::Bind(&MidiManagerAlsa::EventLoop,
317 base::Unretained(this), instance_id_));
agoode@chromium.org25227512014-06-08 05:12:05 +0000318
toyoshimf1b88962015-07-09 14:14:51 -0700319 CompleteInitialization(Result::OK);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000320}
321
toyoshim8e7d6e02015-10-06 08:47:17 -0700322void MidiManagerAlsa::Finalize() {
agoodeb2ead822016-03-11 12:14:35 -0800323 base::AutoLock lock(lazy_init_member_lock_);
324 DCHECK(initialization_thread_checker_->CalledOnValidThread());
325
toyoshimf4d61522017-02-10 02:03:32 -0800326 // Tell tasks running on TaskRunners it will soon be time to shut down. This
327 // gives us assurance a task running on kEventTaskRunner will stop in case the
328 // SND_SEQ_EVENT_CLIENT_EXIT message is lost.
toyoshim8e7d6e02015-10-06 08:47:17 -0700329 {
toyoshimf4d61522017-02-10 02:03:32 -0800330 base::AutoLock lock(g_instance_id_lock.Get());
331 CHECK_EQ(instance_id_, g_active_instance_id);
332 g_active_instance_id = kInvalidInstanceId;
toyoshim8e7d6e02015-10-06 08:47:17 -0700333 }
334
toyoshimf4d61522017-02-10 02:03:32 -0800335 // Ensure that no tasks run on kSendTaskRunner.
336 base::AutoLock send_runner_lock(g_send_task_lock.Get());
toyoshim8e7d6e02015-10-06 08:47:17 -0700337
338 // Close the out client. This will trigger the event thread to stop,
339 // because of SND_SEQ_EVENT_CLIENT_EXIT.
agoodeb2ead822016-03-11 12:14:35 -0800340 out_client_.reset();
toyoshim8e7d6e02015-10-06 08:47:17 -0700341
toyoshimf4d61522017-02-10 02:03:32 -0800342 // Ensure that no tasks run on kEventTaskRunner.
343 base::AutoLock event_runner_lock(g_event_task_lock.Get());
agoodeb2ead822016-03-11 12:14:35 -0800344
345 // Destruct the other stuff we initialized in StartInitialization().
346 udev_monitor_.reset();
347 udev_.reset();
348 decoder_.reset();
349 in_client_.reset();
350 initialization_thread_checker_.reset();
toyoshim8e7d6e02015-10-06 08:47:17 -0700351}
352
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000353void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500354 uint32_t port_index,
355 const std::vector<uint8_t>& data,
dnicoara@chromium.org9f2a6f02014-01-03 21:25:00 +0000356 double timestamp) {
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000357 base::TimeDelta delay;
358 if (timestamp != 0.0) {
359 base::TimeTicks time_to_send =
360 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
agoodebd4be9b2015-03-16 19:17:25 -0700361 timestamp * base::Time::kMicrosecondsPerSecond);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000362 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
363 }
364
toyoshimf4d61522017-02-10 02:03:32 -0800365 service()
366 ->GetTaskRunner(kSendTaskRunner)
367 ->PostDelayedTask(
368 FROM_HERE,
369 base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this),
370 instance_id_, client, port_index, data),
371 delay);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +0000372}
373
agooded87fc0f2015-05-21 08:29:31 -0700374MidiManagerAlsa::MidiPort::Id::Id() = default;
375
376MidiManagerAlsa::MidiPort::Id::Id(const std::string& bus,
377 const std::string& vendor_id,
378 const std::string& model_id,
379 const std::string& usb_interface_num,
380 const std::string& serial)
381 : bus_(bus),
382 vendor_id_(vendor_id),
383 model_id_(model_id),
384 usb_interface_num_(usb_interface_num),
385 serial_(serial) {
386}
387
388MidiManagerAlsa::MidiPort::Id::Id(const Id&) = default;
389
390MidiManagerAlsa::MidiPort::Id::~Id() = default;
391
392bool MidiManagerAlsa::MidiPort::Id::operator==(const Id& rhs) const {
393 return (bus_ == rhs.bus_) && (vendor_id_ == rhs.vendor_id_) &&
394 (model_id_ == rhs.model_id_) &&
395 (usb_interface_num_ == rhs.usb_interface_num_) &&
396 (serial_ == rhs.serial_);
397}
398
399bool MidiManagerAlsa::MidiPort::Id::empty() const {
400 return bus_.empty() && vendor_id_.empty() && model_id_.empty() &&
401 usb_interface_num_.empty() && serial_.empty();
402}
403
agoode99d63292015-04-13 08:39:25 -0700404MidiManagerAlsa::MidiPort::MidiPort(const std::string& path,
agooded87fc0f2015-05-21 08:29:31 -0700405 const Id& id,
agoode99d63292015-04-13 08:39:25 -0700406 int client_id,
407 int port_id,
408 int midi_device,
409 const std::string& client_name,
410 const std::string& port_name,
411 const std::string& manufacturer,
412 const std::string& version,
413 Type type)
414 : id_(id),
415 midi_device_(midi_device),
416 type_(type),
417 path_(path),
418 client_id_(client_id),
419 port_id_(port_id),
420 client_name_(client_name),
421 port_name_(port_name),
422 manufacturer_(manufacturer),
agoode5a1aa112015-06-21 20:51:00 -0700423 version_(version) {
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000424}
425
agoodeb0582872015-05-20 05:22:24 -0700426MidiManagerAlsa::MidiPort::~MidiPort() = default;
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000427
agoode99d63292015-04-13 08:39:25 -0700428// Note: keep synchronized with the MidiPort::Match* methods.
danakj75afea02016-04-25 20:36:04 -0700429std::unique_ptr<base::Value> MidiManagerAlsa::MidiPort::Value() const {
430 std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
agoode99d63292015-04-13 08:39:25 -0700431
432 std::string type;
433 switch (type_) {
434 case Type::kInput:
435 type = "input";
436 break;
agoode99d63292015-04-13 08:39:25 -0700437 case Type::kOutput:
438 type = "output";
439 break;
440 }
441 value->SetString("type", type);
442 SetStringIfNonEmpty(value.get(), "path", path_);
agoode99d63292015-04-13 08:39:25 -0700443 SetStringIfNonEmpty(value.get(), "clientName", client_name_);
444 SetStringIfNonEmpty(value.get(), "portName", port_name_);
445 value->SetInteger("clientId", client_id_);
446 value->SetInteger("portId", port_id_);
447 value->SetInteger("midiDevice", midi_device_);
448
agooded87fc0f2015-05-21 08:29:31 -0700449 // Flatten id fields.
450 SetStringIfNonEmpty(value.get(), "bus", id_.bus());
451 SetStringIfNonEmpty(value.get(), "vendorId", id_.vendor_id());
452 SetStringIfNonEmpty(value.get(), "modelId", id_.model_id());
453 SetStringIfNonEmpty(value.get(), "usbInterfaceNum", id_.usb_interface_num());
454 SetStringIfNonEmpty(value.get(), "serial", id_.serial());
455
dchengc2aeece2015-12-27 00:54:00 -0800456 return std::move(value);
agoodebd4be9b2015-03-16 19:17:25 -0700457}
agoode@chromium.org25227512014-06-08 05:12:05 +0000458
agoode99d63292015-04-13 08:39:25 -0700459std::string MidiManagerAlsa::MidiPort::JSONValue() const {
460 std::string json;
461 JSONStringValueSerializer serializer(&json);
462 serializer.Serialize(*Value().get());
463 return json;
agoodef212b2a2015-03-19 12:53:23 -0700464}
465
agoode99d63292015-04-13 08:39:25 -0700466// TODO(agoode): Do not use SHA256 here. Instead store a persistent
467// mapping and just use a UUID or other random string.
468// http://crbug.com/465320
469std::string MidiManagerAlsa::MidiPort::OpaqueKey() const {
Avi Drissman3528fd02015-12-18 20:11:31 -0500470 uint8_t hash[crypto::kSHA256Length];
agoode99d63292015-04-13 08:39:25 -0700471 crypto::SHA256HashString(JSONValue(), &hash, sizeof(hash));
472 return base::HexEncode(&hash, sizeof(hash));
agoodebd4be9b2015-03-16 19:17:25 -0700473}
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000474
agoode99d63292015-04-13 08:39:25 -0700475bool MidiManagerAlsa::MidiPort::MatchConnected(const MidiPort& query) const {
476 // Matches on:
477 // connected == true
478 // type
479 // path
480 // id
481 // client_id
482 // port_id
483 // midi_device
484 // client_name
485 // port_name
486 return connected() && (type() == query.type()) && (path() == query.path()) &&
487 (id() == query.id()) && (client_id() == query.client_id()) &&
488 (port_id() == query.port_id()) &&
489 (midi_device() == query.midi_device()) &&
490 (client_name() == query.client_name()) &&
491 (port_name() == query.port_name());
agoodebd4be9b2015-03-16 19:17:25 -0700492}
493
agoode99d63292015-04-13 08:39:25 -0700494bool MidiManagerAlsa::MidiPort::MatchCardPass1(const MidiPort& query) const {
495 // Matches on:
496 // connected == false
497 // type
498 // path
499 // id
500 // port_id
501 // midi_device
502 return MatchCardPass2(query) && (path() == query.path());
agoodebd4be9b2015-03-16 19:17:25 -0700503}
504
agoode99d63292015-04-13 08:39:25 -0700505bool MidiManagerAlsa::MidiPort::MatchCardPass2(const MidiPort& query) const {
506 // Matches on:
507 // connected == false
508 // type
509 // id
510 // port_id
511 // midi_device
512 return !connected() && (type() == query.type()) && (id() == query.id()) &&
513 (port_id() == query.port_id()) &&
514 (midi_device() == query.midi_device());
agoodef212b2a2015-03-19 12:53:23 -0700515}
516
agoode99d63292015-04-13 08:39:25 -0700517bool MidiManagerAlsa::MidiPort::MatchNoCardPass1(const MidiPort& query) const {
518 // Matches on:
519 // connected == false
520 // type
521 // path.empty(), for both this and query
522 // id.empty(), for both this and query
523 // client_id
524 // port_id
525 // client_name
526 // port_name
527 // midi_device == -1, for both this and query
528 return MatchNoCardPass2(query) && (client_id() == query.client_id());
agoodef212b2a2015-03-19 12:53:23 -0700529}
530
agoode99d63292015-04-13 08:39:25 -0700531bool MidiManagerAlsa::MidiPort::MatchNoCardPass2(const MidiPort& query) const {
532 // Matches on:
533 // connected == false
534 // type
535 // path.empty(), for both this and query
536 // id.empty(), for both this and query
537 // port_id
538 // client_name
539 // port_name
540 // midi_device == -1, for both this and query
541 return !connected() && (type() == query.type()) && path().empty() &&
542 query.path().empty() && id().empty() && query.id().empty() &&
543 (port_id() == query.port_id()) &&
544 (client_name() == query.client_name()) &&
545 (port_name() == query.port_name()) && (midi_device() == -1) &&
546 (query.midi_device() == -1);
toyoshim@chromium.org4a8657c2014-02-06 11:23:09 +0000547}
548
agoodeb0582872015-05-20 05:22:24 -0700549MidiManagerAlsa::MidiPortStateBase::~MidiPortStateBase() = default;
agoode99d63292015-04-13 08:39:25 -0700550
551MidiManagerAlsa::MidiPortStateBase::iterator
552MidiManagerAlsa::MidiPortStateBase::Find(
553 const MidiManagerAlsa::MidiPort& port) {
554 auto result = FindConnected(port);
555 if (result == end())
556 result = FindDisconnected(port);
557 return result;
558}
559
560MidiManagerAlsa::MidiPortStateBase::iterator
561MidiManagerAlsa::MidiPortStateBase::FindConnected(
562 const MidiManagerAlsa::MidiPort& port) {
563 // Exact match required for connected ports.
danakj75afea02016-04-25 20:36:04 -0700564 auto it = std::find_if(ports_.begin(), ports_.end(),
565 [&port](std::unique_ptr<MidiPort>& p) {
566 return p->MatchConnected(port);
567 });
agoode99d63292015-04-13 08:39:25 -0700568 return it;
569}
570
571MidiManagerAlsa::MidiPortStateBase::iterator
572MidiManagerAlsa::MidiPortStateBase::FindDisconnected(
573 const MidiManagerAlsa::MidiPort& port) {
574 // Always match on:
575 // type
576 // Possible things to match on:
577 // path
578 // id
579 // client_id
580 // port_id
581 // midi_device
582 // client_name
583 // port_name
584
585 if (!port.path().empty()) {
586 // If path is present, then we have a card-based client.
587
588 // Pass 1. Match on path, id, midi_device, port_id.
589 // This is the best possible match for hardware card-based clients.
590 // This will also match the empty id correctly for devices without an id.
danakj75afea02016-04-25 20:36:04 -0700591 auto it = std::find_if(ports_.begin(), ports_.end(),
592 [&port](std::unique_ptr<MidiPort>& p) {
593 return p->MatchCardPass1(port);
594 });
agoode99d63292015-04-13 08:39:25 -0700595 if (it != ports_.end())
596 return it;
597
598 if (!port.id().empty()) {
599 // Pass 2. Match on id, midi_device, port_id.
600 // This will give us a high-confidence match when a user moves a device to
601 // another USB/Firewire/Thunderbolt/etc port, but only works if the device
602 // has a hardware id.
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->MatchCardPass2(port);
606 });
agoode99d63292015-04-13 08:39:25 -0700607 if (it != ports_.end())
608 return it;
609 }
610 } else {
611 // Else, we have a non-card-based client.
612 // Pass 1. Match on client_id, port_id, client_name, port_name.
613 // This will give us a reasonably good match.
danakj75afea02016-04-25 20:36:04 -0700614 auto it = std::find_if(ports_.begin(), ports_.end(),
615 [&port](std::unique_ptr<MidiPort>& p) {
616 return p->MatchNoCardPass1(port);
617 });
agoode99d63292015-04-13 08:39:25 -0700618 if (it != ports_.end())
619 return it;
620
621 // Pass 2. Match on port_id, client_name, port_name.
622 // This is weaker but similar to pass 2 in the hardware card-based clients
623 // match.
danakj75afea02016-04-25 20:36:04 -0700624 it = std::find_if(ports_.begin(), ports_.end(),
625 [&port](std::unique_ptr<MidiPort>& p) {
626 return p->MatchNoCardPass2(port);
627 });
agoode99d63292015-04-13 08:39:25 -0700628 if (it != ports_.end())
629 return it;
630 }
631
632 // No match.
633 return ports_.end();
634}
635
agoodeb0582872015-05-20 05:22:24 -0700636MidiManagerAlsa::MidiPortStateBase::MidiPortStateBase() = default;
agoode99d63292015-04-13 08:39:25 -0700637
agoodedf1b9ff2015-06-25 18:14:50 -0700638MidiManagerAlsa::MidiPortState::MidiPortState() = default;
agoode99d63292015-04-13 08:39:25 -0700639
danakj75afea02016-04-25 20:36:04 -0700640uint32_t MidiManagerAlsa::MidiPortState::push_back(
641 std::unique_ptr<MidiPort> port) {
agoode99d63292015-04-13 08:39:25 -0700642 // Add the web midi index.
Avi Drissman3528fd02015-12-18 20:11:31 -0500643 uint32_t web_port_index = 0;
agoode99d63292015-04-13 08:39:25 -0700644 switch (port->type()) {
645 case MidiPort::Type::kInput:
646 web_port_index = num_input_ports_++;
647 break;
648 case MidiPort::Type::kOutput:
649 web_port_index = num_output_ports_++;
650 break;
651 }
652 port->set_web_port_index(web_port_index);
dchengc2aeece2015-12-27 00:54:00 -0800653 MidiPortStateBase::push_back(std::move(port));
agoode99d63292015-04-13 08:39:25 -0700654 return web_port_index;
655}
656
agoodedf1b9ff2015-06-25 18:14:50 -0700657MidiManagerAlsa::AlsaSeqState::AlsaSeqState() = default;
agoode99d63292015-04-13 08:39:25 -0700658
agoodeb0582872015-05-20 05:22:24 -0700659MidiManagerAlsa::AlsaSeqState::~AlsaSeqState() = default;
agoode99d63292015-04-13 08:39:25 -0700660
661void MidiManagerAlsa::AlsaSeqState::ClientStart(int client_id,
662 const std::string& client_name,
663 snd_seq_client_type_t type) {
664 ClientExit(client_id);
ricea37e45762016-08-25 02:43:39 -0700665 clients_.insert(
666 std::make_pair(client_id, base::MakeUnique<Client>(client_name, type)));
agoodeb09423b2015-05-11 11:39:57 -0700667 if (IsCardClient(type, client_id))
668 ++card_client_count_;
agoode99d63292015-04-13 08:39:25 -0700669}
670
671bool MidiManagerAlsa::AlsaSeqState::ClientStarted(int client_id) {
672 return clients_.find(client_id) != clients_.end();
673}
674
675void MidiManagerAlsa::AlsaSeqState::ClientExit(int client_id) {
676 auto it = clients_.find(client_id);
677 if (it != clients_.end()) {
agoodeb09423b2015-05-11 11:39:57 -0700678 if (IsCardClient(it->second->type(), client_id))
679 --card_client_count_;
agoode99d63292015-04-13 08:39:25 -0700680 clients_.erase(it);
681 }
682}
683
684void MidiManagerAlsa::AlsaSeqState::PortStart(
685 int client_id,
686 int port_id,
687 const std::string& port_name,
688 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
689 bool midi) {
690 auto it = clients_.find(client_id);
691 if (it != clients_.end())
692 it->second->AddPort(port_id,
ricea37e45762016-08-25 02:43:39 -0700693 base::MakeUnique<Port>(port_name, direction, midi));
agoode99d63292015-04-13 08:39:25 -0700694}
695
696void MidiManagerAlsa::AlsaSeqState::PortExit(int client_id, int port_id) {
697 auto it = clients_.find(client_id);
698 if (it != clients_.end())
699 it->second->RemovePort(port_id);
700}
701
702snd_seq_client_type_t MidiManagerAlsa::AlsaSeqState::ClientType(
703 int client_id) const {
704 auto it = clients_.find(client_id);
705 if (it == clients_.end())
706 return SND_SEQ_USER_CLIENT;
707 return it->second->type();
708}
709
danakj75afea02016-04-25 20:36:04 -0700710std::unique_ptr<MidiManagerAlsa::TemporaryMidiPortState>
agoodeb09423b2015-05-11 11:39:57 -0700711MidiManagerAlsa::AlsaSeqState::ToMidiPortState(const AlsaCardMap& alsa_cards) {
danakj75afea02016-04-25 20:36:04 -0700712 std::unique_ptr<MidiManagerAlsa::TemporaryMidiPortState> midi_ports(
agoode99d63292015-04-13 08:39:25 -0700713 new TemporaryMidiPortState);
agoodeb09423b2015-05-11 11:39:57 -0700714 auto card_it = alsa_cards.begin();
agoode99d63292015-04-13 08:39:25 -0700715
agoodeb09423b2015-05-11 11:39:57 -0700716 int card_midi_device = -1;
agoode99d63292015-04-13 08:39:25 -0700717 for (const auto& client_pair : clients_) {
718 int client_id = client_pair.first;
vmpstr0205abb2016-06-28 18:50:56 -0700719 auto* client = client_pair.second.get();
agoode99d63292015-04-13 08:39:25 -0700720
721 // Get client metadata.
722 const std::string client_name = client->name();
723 std::string manufacturer;
724 std::string driver;
725 std::string path;
agooded87fc0f2015-05-21 08:29:31 -0700726 MidiPort::Id id;
agoode99d63292015-04-13 08:39:25 -0700727 std::string card_name;
728 std::string card_longname;
729 int midi_device = -1;
730
agoodeb09423b2015-05-11 11:39:57 -0700731 if (IsCardClient(client->type(), client_id)) {
732 auto& card = card_it->second;
733 if (card_midi_device == -1)
734 card_midi_device = 0;
735
736 manufacturer = card->manufacturer();
agooded87fc0f2015-05-21 08:29:31 -0700737 path = card->path();
738 id = MidiPort::Id(card->bus(), card->vendor_id(), card->model_id(),
739 card->usb_interface_num(), card->serial());
740 card_name = card->name();
741 card_longname = card->longname();
agoodeb09423b2015-05-11 11:39:57 -0700742 midi_device = card_midi_device;
743
744 ++card_midi_device;
745 if (card_midi_device >= card->midi_device_count()) {
746 card_midi_device = -1;
747 ++card_it;
748 }
749 }
750
agoode99d63292015-04-13 08:39:25 -0700751 for (const auto& port_pair : *client) {
752 int port_id = port_pair.first;
753 const auto& port = port_pair.second;
754
755 if (port->midi()) {
756 std::string version;
757 if (!driver.empty()) {
758 version = driver + " / ";
759 }
760 version +=
761 base::StringPrintf("ALSA library version %d.%d.%d", SND_LIB_MAJOR,
762 SND_LIB_MINOR, SND_LIB_SUBMINOR);
763 PortDirection direction = port->direction();
764 if (direction == PortDirection::kInput ||
765 direction == PortDirection::kDuplex) {
ricea37e45762016-08-25 02:43:39 -0700766 midi_ports->push_back(base::MakeUnique<MidiPort>(
agoode99d63292015-04-13 08:39:25 -0700767 path, id, client_id, port_id, midi_device, client->name(),
ricea37e45762016-08-25 02:43:39 -0700768 port->name(), manufacturer, version, MidiPort::Type::kInput));
agoode99d63292015-04-13 08:39:25 -0700769 }
770 if (direction == PortDirection::kOutput ||
771 direction == PortDirection::kDuplex) {
ricea37e45762016-08-25 02:43:39 -0700772 midi_ports->push_back(base::MakeUnique<MidiPort>(
agoode99d63292015-04-13 08:39:25 -0700773 path, id, client_id, port_id, midi_device, client->name(),
ricea37e45762016-08-25 02:43:39 -0700774 port->name(), manufacturer, version, MidiPort::Type::kOutput));
agoode99d63292015-04-13 08:39:25 -0700775 }
776 }
777 }
778 }
779
dchengc2aeece2015-12-27 00:54:00 -0800780 return midi_ports;
agoode99d63292015-04-13 08:39:25 -0700781}
782
783MidiManagerAlsa::AlsaSeqState::Port::Port(
784 const std::string& name,
785 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
786 bool midi)
787 : name_(name), direction_(direction), midi_(midi) {
788}
789
agoodeb0582872015-05-20 05:22:24 -0700790MidiManagerAlsa::AlsaSeqState::Port::~Port() = default;
agoode99d63292015-04-13 08:39:25 -0700791
792MidiManagerAlsa::AlsaSeqState::Client::Client(const std::string& name,
793 snd_seq_client_type_t type)
mgiucad9af8452015-06-25 02:11:57 -0700794 : name_(name), type_(type) {
agoode99d63292015-04-13 08:39:25 -0700795}
796
agoodeb0582872015-05-20 05:22:24 -0700797MidiManagerAlsa::AlsaSeqState::Client::~Client() = default;
agoode99d63292015-04-13 08:39:25 -0700798
danakj75afea02016-04-25 20:36:04 -0700799void MidiManagerAlsa::AlsaSeqState::Client::AddPort(
800 int addr,
801 std::unique_ptr<Port> port) {
limasdfe59d0392015-11-19 20:28:57 -0800802 ports_[addr] = std::move(port);
agoode99d63292015-04-13 08:39:25 -0700803}
804
805void MidiManagerAlsa::AlsaSeqState::Client::RemovePort(int addr) {
mgiucad9af8452015-06-25 02:11:57 -0700806 ports_.erase(addr);
agoode99d63292015-04-13 08:39:25 -0700807}
808
809MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
810MidiManagerAlsa::AlsaSeqState::Client::begin() const {
811 return ports_.begin();
812}
813
814MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
815MidiManagerAlsa::AlsaSeqState::Client::end() const {
816 return ports_.end();
agoodeaf6e9f52015-03-24 10:23:49 -0700817}
818
agoodeb09423b2015-05-11 11:39:57 -0700819MidiManagerAlsa::AlsaCard::AlsaCard(udev_device* dev,
agooded87fc0f2015-05-21 08:29:31 -0700820 const std::string& name,
821 const std::string& longname,
822 const std::string& driver,
agoodeb09423b2015-05-11 11:39:57 -0700823 int midi_device_count)
agooded87fc0f2015-05-21 08:29:31 -0700824 : name_(name),
825 longname_(longname),
826 driver_(driver),
827 path_(device::UdevDeviceGetPropertyValue(dev, kUdevIdPath)),
828 bus_(device::UdevDeviceGetPropertyValue(dev, kUdevIdBus)),
829 vendor_id_(
830 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendorId, kSysattrVendor)),
831 model_id_(
832 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdModelId, kSysattrModel)),
833 usb_interface_num_(
834 device::UdevDeviceGetPropertyValue(dev, kUdevIdUsbInterfaceNum)),
835 serial_(UdevDeviceGetPropertyOrSysattr(dev,
836 kUdevIdSerialShort,
837 kSysattrGuid)),
838 midi_device_count_(midi_device_count),
839 manufacturer_(ExtractManufacturerString(
840 GetVendor(dev),
841 vendor_id_,
842 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorFromDatabase),
843 name,
844 longname)) {
agoodeb09423b2015-05-11 11:39:57 -0700845}
846
agoodeb0582872015-05-20 05:22:24 -0700847MidiManagerAlsa::AlsaCard::~AlsaCard() = default;
agoodeb09423b2015-05-11 11:39:57 -0700848
agoode55a8b522015-03-08 12:40:17 -0700849// static
agoodeb09423b2015-05-11 11:39:57 -0700850std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
agoode5e4e9cd2015-03-09 12:34:24 -0700851 const std::string& udev_id_vendor,
agoode55a8b522015-03-08 12:40:17 -0700852 const std::string& udev_id_vendor_id,
853 const std::string& udev_id_vendor_from_database,
854 const std::string& alsa_name,
855 const std::string& alsa_longname) {
856 // Let's try to determine the manufacturer. Here is the ordered preference
857 // in extraction:
agoodef212b2a2015-03-19 12:53:23 -0700858 // 1. Vendor name from the hardware device string, from udev properties
859 // or sysattrs.
agoode5e4e9cd2015-03-09 12:34:24 -0700860 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
agoode55a8b522015-03-08 12:40:17 -0700861 // 3. Heuristic from ALSA.
862
agoodee83758c2015-03-23 22:07:54 -0700863 // Is the vendor string present and not just the vendor hex id?
864 if (!udev_id_vendor.empty() && (udev_id_vendor != udev_id_vendor_id)) {
agoode55a8b522015-03-08 12:40:17 -0700865 return udev_id_vendor;
866 }
867
868 // Is there a vendor string in the hardware database?
869 if (!udev_id_vendor_from_database.empty()) {
870 return udev_id_vendor_from_database;
871 }
872
873 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
874 // We assume that card longname is in the format of
875 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
876 // a manufacturer name here.
877 size_t at_index = alsa_longname.rfind(" at ");
agoodef212b2a2015-03-19 12:53:23 -0700878 if (at_index && at_index != std::string::npos) {
agoode55a8b522015-03-08 12:40:17 -0700879 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
agoodef212b2a2015-03-19 12:53:23 -0700880 if (name_index && name_index != std::string::npos)
agoode55a8b522015-03-08 12:40:17 -0700881 return alsa_longname.substr(0, name_index - 1);
882 }
883
884 // Failure.
885 return "";
886}
887
toyoshimf4d61522017-02-10 02:03:32 -0800888void MidiManagerAlsa::SendMidiData(int instance_id,
889 MidiManagerClient* client,
890 uint32_t port_index,
Avi Drissman3528fd02015-12-18 20:11:31 -0500891 const std::vector<uint8_t>& data) {
toyoshimf4d61522017-02-10 02:03:32 -0800892 DCHECK(service()->GetTaskRunner(kSendTaskRunner)->BelongsToCurrentThread());
893
894 // Obtain the lock so that the instance could not be destructed while this
895 // method is running on the kSendTaskRunner.
896 base::AutoLock lock(g_send_task_lock.Get());
897 {
898 // Check if Finalize() already runs. After this check, we can access |this|
899 // safely on the kEventTaskRunner.
900 base::AutoLock instance_id_lock(g_instance_id_lock.Get());
901 if (instance_id != g_active_instance_id)
902 return;
903 }
agoodebd4be9b2015-03-16 19:17:25 -0700904
agoodef212b2a2015-03-19 12:53:23 -0700905 snd_midi_event_t* encoder;
906 snd_midi_event_new(kSendBufferSize, &encoder);
agoode5a1aa112015-06-21 20:51:00 -0700907 for (const auto datum : data) {
agoodebd4be9b2015-03-16 19:17:25 -0700908 snd_seq_event_t event;
agoode5a1aa112015-06-21 20:51:00 -0700909 int result = snd_midi_event_encode_byte(encoder, datum, &event);
agoodebd4be9b2015-03-16 19:17:25 -0700910 if (result == 1) {
911 // Full event, send it.
agoode99d63292015-04-13 08:39:25 -0700912 base::AutoLock lock(out_ports_lock_);
913 auto it = out_ports_.find(port_index);
914 if (it != out_ports_.end()) {
915 snd_seq_ev_set_source(&event, it->second);
916 snd_seq_ev_set_subs(&event);
917 snd_seq_ev_set_direct(&event);
agoode5a1aa112015-06-21 20:51:00 -0700918 snd_seq_event_output_direct(out_client_.get(), &event);
agoode99d63292015-04-13 08:39:25 -0700919 }
agoodebd4be9b2015-03-16 19:17:25 -0700920 }
921 }
agoodef212b2a2015-03-19 12:53:23 -0700922 snd_midi_event_free(encoder);
toyoshimf4d61522017-02-10 02:03:32 -0800923
924 // Acknowledge send.
925 AccumulateMidiBytesSent(client, data.size());
agoodebd4be9b2015-03-16 19:17:25 -0700926}
927
toyoshimf4d61522017-02-10 02:03:32 -0800928void MidiManagerAlsa::EventLoop(int instance_id) {
929 // Obtain the lock so that the instance could not be destructed while this
930 // method is running on the kEventTaskRunner.
931 base::AutoLock lock(g_event_task_lock.Get());
932 {
933 // Check if Finalize() already runs. After this check, we can access |this|
934 // safely on the kEventTaskRunner.
935 base::AutoLock instance_id_lock(g_instance_id_lock.Get());
936 if (instance_id != g_active_instance_id)
937 return;
938 }
agoodebd4be9b2015-03-16 19:17:25 -0700939
agoode975043d2015-05-11 00:46:17 -0700940 bool loop_again = true;
agoodebd4be9b2015-03-16 19:17:25 -0700941
agoode975043d2015-05-11 00:46:17 -0700942 struct pollfd pfd[2];
agoode5a1aa112015-06-21 20:51:00 -0700943 snd_seq_poll_descriptors(in_client_.get(), &pfd[0], 1, POLLIN);
agoode975043d2015-05-11 00:46:17 -0700944 pfd[1].fd = device::udev_monitor_get_fd(udev_monitor_.get());
945 pfd[1].events = POLLIN;
agoodebd4be9b2015-03-16 19:17:25 -0700946
agoode975043d2015-05-11 00:46:17 -0700947 int err = HANDLE_EINTR(poll(pfd, arraysize(pfd), -1));
948 if (err < 0) {
brettwf7f870f2015-06-09 11:05:24 -0700949 VLOG(1) << "poll fails: " << base::safe_strerror(errno);
agoode975043d2015-05-11 00:46:17 -0700950 loop_again = false;
agoodeaf6e9f52015-03-24 10:23:49 -0700951 } else {
agoode975043d2015-05-11 00:46:17 -0700952 if (pfd[0].revents & POLLIN) {
953 // Read available incoming MIDI data.
954 int remaining;
955 double timestamp =
956 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
957 do {
958 snd_seq_event_t* event;
agoode5a1aa112015-06-21 20:51:00 -0700959 err = snd_seq_event_input(in_client_.get(), &event);
960 remaining = snd_seq_event_input_pending(in_client_.get(), 0);
agoode975043d2015-05-11 00:46:17 -0700961
962 if (err == -ENOSPC) {
963 // Handle out of space error.
964 VLOG(1) << "snd_seq_event_input detected buffer overrun";
965 // We've lost events: check another way to see if we need to shut
966 // down.
agoode975043d2015-05-11 00:46:17 -0700967 } else if (err == -EAGAIN) {
968 // We've read all the data.
969 } else if (err < 0) {
970 // Handle other errors.
971 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
972 // TODO(agoode): Use RecordAction() or similar to log this.
973 loop_again = false;
974 } else if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
975 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
976 // Handle announce events.
977 switch (event->type) {
978 case SND_SEQ_EVENT_PORT_START:
979 // Don't use SND_SEQ_EVENT_CLIENT_START because the
980 // client name may not be set by the time we query
981 // it. It should be set by the time ports are made.
982 ProcessClientStartEvent(event->data.addr.client);
983 ProcessPortStartEvent(event->data.addr);
984 break;
985 case SND_SEQ_EVENT_CLIENT_EXIT:
986 // Check for disconnection of our "out" client. This means "shut
987 // down".
988 if (event->data.addr.client == out_client_id_) {
989 loop_again = false;
990 remaining = 0;
991 } else
992 ProcessClientExitEvent(event->data.addr);
993 break;
994 case SND_SEQ_EVENT_PORT_EXIT:
995 ProcessPortExitEvent(event->data.addr);
996 break;
997 }
998 } else {
999 // Normal operation.
1000 ProcessSingleEvent(event, timestamp);
1001 }
1002 } while (remaining > 0);
1003 }
1004 if (pfd[1].revents & POLLIN) {
1005 device::ScopedUdevDevicePtr dev(
1006 device::udev_monitor_receive_device(udev_monitor_.get()));
1007 if (dev.get())
1008 ProcessUdevEvent(dev.get());
1009 else
1010 VLOG(1) << "udev_monitor_receive_device fails";
1011 }
agoodebd4be9b2015-03-16 19:17:25 -07001012 }
1013
agoodebd4be9b2015-03-16 19:17:25 -07001014 // Do again.
toyoshimf4d61522017-02-10 02:03:32 -08001015 if (loop_again) {
1016 service()
1017 ->GetTaskRunner(kEventTaskRunner)
1018 ->PostTask(FROM_HERE, base::Bind(&MidiManagerAlsa::EventLoop,
1019 base::Unretained(this), instance_id));
1020 }
agoodebd4be9b2015-03-16 19:17:25 -07001021}
1022
1023void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
1024 double timestamp) {
agoode99d63292015-04-13 08:39:25 -07001025 auto source_it =
1026 source_map_.find(AddrToInt(event->source.client, event->source.port));
agoodebd4be9b2015-03-16 19:17:25 -07001027 if (source_it != source_map_.end()) {
Avi Drissman3528fd02015-12-18 20:11:31 -05001028 uint32_t source = source_it->second;
agoodebd4be9b2015-03-16 19:17:25 -07001029 if (event->type == SND_SEQ_EVENT_SYSEX) {
1030 // Special! Variable-length sysex.
Avi Drissman3528fd02015-12-18 20:11:31 -05001031 ReceiveMidiData(source, static_cast<const uint8_t*>(event->data.ext.ptr),
agoodebd4be9b2015-03-16 19:17:25 -07001032 event->data.ext.len, timestamp);
1033 } else {
1034 // Otherwise, decode this and send that on.
1035 unsigned char buf[12];
agoode5a1aa112015-06-21 20:51:00 -07001036 long count =
1037 snd_midi_event_decode(decoder_.get(), buf, sizeof(buf), event);
agoodebd4be9b2015-03-16 19:17:25 -07001038 if (count <= 0) {
1039 if (count != -ENOENT) {
1040 // ENOENT means that it's not a MIDI message, which is not an
1041 // error, but other negative values are errors for us.
1042 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
1043 // TODO(agoode): Record this failure.
1044 }
1045 } else {
1046 ReceiveMidiData(source, buf, count, timestamp);
1047 }
1048 }
1049 }
1050}
1051
agoode99d63292015-04-13 08:39:25 -07001052void MidiManagerAlsa::ProcessClientStartEvent(int client_id) {
1053 // Ignore if client is already started.
1054 if (alsa_seq_state_.ClientStarted(client_id))
1055 return;
1056
1057 snd_seq_client_info_t* client_info;
1058 snd_seq_client_info_alloca(&client_info);
agoode5a1aa112015-06-21 20:51:00 -07001059 int err =
1060 snd_seq_get_any_client_info(in_client_.get(), client_id, client_info);
agoode99d63292015-04-13 08:39:25 -07001061 if (err != 0)
1062 return;
1063
1064 // Skip our own clients.
1065 if ((client_id == in_client_id_) || (client_id == out_client_id_))
1066 return;
1067
1068 // Update our view of ALSA seq state.
1069 alsa_seq_state_.ClientStart(client_id,
1070 snd_seq_client_info_get_name(client_info),
1071 snd_seq_client_info_get_type(client_info));
1072
1073 // Generate Web MIDI events.
1074 UpdatePortStateAndGenerateEvents();
1075}
1076
1077void MidiManagerAlsa::ProcessPortStartEvent(const snd_seq_addr_t& addr) {
1078 snd_seq_port_info_t* port_info;
1079 snd_seq_port_info_alloca(&port_info);
agoode5a1aa112015-06-21 20:51:00 -07001080 int err = snd_seq_get_any_port_info(in_client_.get(), addr.client, addr.port,
1081 port_info);
agoode99d63292015-04-13 08:39:25 -07001082 if (err != 0)
1083 return;
1084
1085 unsigned int caps = snd_seq_port_info_get_capability(port_info);
1086 bool input = (caps & kRequiredInputPortCaps) == kRequiredInputPortCaps;
1087 bool output = (caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps;
1088 AlsaSeqState::PortDirection direction;
1089 if (input && output)
1090 direction = AlsaSeqState::PortDirection::kDuplex;
1091 else if (input)
1092 direction = AlsaSeqState::PortDirection::kInput;
1093 else if (output)
1094 direction = AlsaSeqState::PortDirection::kOutput;
1095 else
1096 return;
1097
1098 // Update our view of ALSA seq state.
1099 alsa_seq_state_.PortStart(
1100 addr.client, addr.port, snd_seq_port_info_get_name(port_info), direction,
1101 snd_seq_port_info_get_type(port_info) & SND_SEQ_PORT_TYPE_MIDI_GENERIC);
1102 // Generate Web MIDI events.
1103 UpdatePortStateAndGenerateEvents();
1104}
1105
1106void MidiManagerAlsa::ProcessClientExitEvent(const snd_seq_addr_t& addr) {
1107 // Update our view of ALSA seq state.
1108 alsa_seq_state_.ClientExit(addr.client);
1109 // Generate Web MIDI events.
1110 UpdatePortStateAndGenerateEvents();
1111}
1112
1113void MidiManagerAlsa::ProcessPortExitEvent(const snd_seq_addr_t& addr) {
1114 // Update our view of ALSA seq state.
1115 alsa_seq_state_.PortExit(addr.client, addr.port);
1116 // Generate Web MIDI events.
1117 UpdatePortStateAndGenerateEvents();
1118}
1119
agoode975043d2015-05-11 00:46:17 -07001120void MidiManagerAlsa::ProcessUdevEvent(udev_device* dev) {
1121 // Only card devices have this property set, and only when they are
1122 // fully initialized.
1123 if (!device::udev_device_get_property_value(dev,
1124 kUdevPropertySoundInitialized))
1125 return;
1126
1127 // Get the action. If no action, then we are doing first time enumeration
1128 // and the device is treated as new.
1129 const char* action = device::udev_device_get_action(dev);
1130 if (!action)
1131 action = kUdevActionChange;
1132
1133 if (strcmp(action, kUdevActionChange) == 0) {
agoodeb09423b2015-05-11 11:39:57 -07001134 AddCard(dev);
1135 // Generate Web MIDI events.
1136 UpdatePortStateAndGenerateEvents();
agoode975043d2015-05-11 00:46:17 -07001137 } else if (strcmp(action, kUdevActionRemove) == 0) {
agoodeb09423b2015-05-11 11:39:57 -07001138 RemoveCard(GetCardNumber(dev));
1139 // Generate Web MIDI events.
1140 UpdatePortStateAndGenerateEvents();
agoode975043d2015-05-11 00:46:17 -07001141 }
1142}
1143
agoodeb09423b2015-05-11 11:39:57 -07001144void MidiManagerAlsa::AddCard(udev_device* dev) {
1145 int number = GetCardNumber(dev);
1146 if (number == -1)
1147 return;
1148
1149 RemoveCard(number);
1150
1151 snd_ctl_card_info_t* card;
1152 snd_hwdep_info_t* hwdep;
1153 snd_ctl_card_info_alloca(&card);
1154 snd_hwdep_info_alloca(&hwdep);
1155 const std::string id = base::StringPrintf("hw:CARD=%i", number);
1156 snd_ctl_t* handle;
1157 int err = snd_ctl_open(&handle, id.c_str(), 0);
1158 if (err != 0) {
1159 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
1160 return;
1161 }
1162 err = snd_ctl_card_info(handle, card);
1163 if (err != 0) {
1164 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
1165 snd_ctl_close(handle);
1166 return;
1167 }
1168 std::string name = snd_ctl_card_info_get_name(card);
1169 std::string longname = snd_ctl_card_info_get_longname(card);
1170 std::string driver = snd_ctl_card_info_get_driver(card);
1171
1172 // Count rawmidi devices (not subdevices).
1173 int midi_count = 0;
1174 for (int device = -1;
1175 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;)
1176 ++midi_count;
1177
1178 // Count any hwdep synths that become MIDI devices outside of rawmidi.
1179 //
1180 // Explanation:
1181 // Any kernel driver can create an ALSA client (visible to us).
1182 // With modern hardware, only rawmidi devices do this. Kernel
1183 // drivers create rawmidi devices and the rawmidi subsystem makes
1184 // the seq clients. But the OPL3 driver is special, it does not
1185 // make a rawmidi device but a seq client directly. (This is the
1186 // only one to worry about in the kernel code, as of 2015-03-23.)
1187 //
1188 // OPL3 is very old (but still possible to get in new
1189 // hardware). It is unlikely that new drivers would not use
1190 // rawmidi and defeat our heuristic.
1191 //
1192 // Longer term, support should be added in the kernel to expose a
1193 // direct link from card->client (or client->card) so that all
1194 // these heuristics will be obsolete. Once that is there, we can
1195 // assume our old heuristics will work on old kernels and the new
1196 // robust code will be used on new. Then we will not need to worry
1197 // about changes to kernel internals breaking our code.
1198 // See the TODO above at kMinimumClientIdForCards.
1199 for (int device = -1;
1200 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) {
1201 err = snd_ctl_hwdep_info(handle, hwdep);
1202 if (err != 0) {
1203 VLOG(1) << "snd_ctl_hwdep_info fails: " << snd_strerror(err);
1204 continue;
1205 }
1206 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep);
1207 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 ||
1208 iface == SND_HWDEP_IFACE_OPL4)
1209 ++midi_count;
1210 }
1211 snd_ctl_close(handle);
1212
mgiucad9af8452015-06-25 02:11:57 -07001213 if (midi_count > 0) {
danakj75afea02016-04-25 20:36:04 -07001214 std::unique_ptr<AlsaCard> card(
mgiucad9af8452015-06-25 02:11:57 -07001215 new AlsaCard(dev, name, longname, driver, midi_count));
limasdfe59d0392015-11-19 20:28:57 -08001216 alsa_cards_.insert(std::make_pair(number, std::move(card)));
mgiucad9af8452015-06-25 02:11:57 -07001217 alsa_card_midi_count_ += midi_count;
1218 }
agoodeb09423b2015-05-11 11:39:57 -07001219}
1220
1221void MidiManagerAlsa::RemoveCard(int number) {
1222 auto it = alsa_cards_.find(number);
1223 if (it == alsa_cards_.end())
1224 return;
1225
1226 alsa_card_midi_count_ -= it->second->midi_device_count();
agoodeb09423b2015-05-11 11:39:57 -07001227 alsa_cards_.erase(it);
1228}
1229
agoode99d63292015-04-13 08:39:25 -07001230void MidiManagerAlsa::UpdatePortStateAndGenerateEvents() {
agoodeb09423b2015-05-11 11:39:57 -07001231 // Verify that our information from ALSA and udev are in sync. If
1232 // not, we cannot generate events right now.
1233 if (alsa_card_midi_count_ != alsa_seq_state_.card_client_count())
1234 return;
1235
agoode99d63292015-04-13 08:39:25 -07001236 // Generate new port state.
agoodeb09423b2015-05-11 11:39:57 -07001237 auto new_port_state = alsa_seq_state_.ToMidiPortState(alsa_cards_);
agoode99d63292015-04-13 08:39:25 -07001238
1239 // Disconnect any connected old ports that are now missing.
agoode5ebc4932015-12-01 08:25:12 -08001240 for (auto& old_port : port_state_) {
agoode99d63292015-04-13 08:39:25 -07001241 if (old_port->connected() &&
1242 (new_port_state->FindConnected(*old_port) == new_port_state->end())) {
1243 old_port->set_connected(false);
Avi Drissman3528fd02015-12-18 20:11:31 -05001244 uint32_t web_port_index = old_port->web_port_index();
agoode99d63292015-04-13 08:39:25 -07001245 switch (old_port->type()) {
1246 case MidiPort::Type::kInput:
1247 source_map_.erase(
1248 AddrToInt(old_port->client_id(), old_port->port_id()));
toyoshimec2570a2016-10-21 02:15:27 -07001249 SetInputPortState(web_port_index, PortState::DISCONNECTED);
agoode99d63292015-04-13 08:39:25 -07001250 break;
1251 case MidiPort::Type::kOutput:
1252 DeleteAlsaOutputPort(web_port_index);
toyoshimec2570a2016-10-21 02:15:27 -07001253 SetOutputPortState(web_port_index, PortState::DISCONNECTED);
agoode99d63292015-04-13 08:39:25 -07001254 break;
1255 }
1256 }
1257 }
1258
1259 // Reconnect or add new ports.
1260 auto it = new_port_state->begin();
1261 while (it != new_port_state->end()) {
agoode5ebc4932015-12-01 08:25:12 -08001262 auto& new_port = *it;
agoode99d63292015-04-13 08:39:25 -07001263 auto old_port = port_state_.Find(*new_port);
1264 if (old_port == port_state_.end()) {
1265 // Add new port.
agoode5ebc4932015-12-01 08:25:12 -08001266 const auto& opaque_key = new_port->OpaqueKey();
1267 const auto& manufacturer = new_port->manufacturer();
1268 const auto& port_name = new_port->port_name();
1269 const auto& version = new_port->version();
1270 const auto& type = new_port->type();
1271 const auto& client_id = new_port->client_id();
1272 const auto& port_id = new_port->port_id();
1273
dchengc2aeece2015-12-27 00:54:00 -08001274 uint32_t web_port_index = port_state_.push_back(std::move(new_port));
agoode5ebc4932015-12-01 08:25:12 -08001275 it = new_port_state->erase(it);
1276
1277 MidiPortInfo info(opaque_key, manufacturer, port_name, version,
toyoshimec2570a2016-10-21 02:15:27 -07001278 PortState::OPENED);
agoode5ebc4932015-12-01 08:25:12 -08001279 switch (type) {
agoode99d63292015-04-13 08:39:25 -07001280 case MidiPort::Type::kInput:
agoode5ebc4932015-12-01 08:25:12 -08001281 if (Subscribe(web_port_index, client_id, port_id))
agoode99d63292015-04-13 08:39:25 -07001282 AddInputPort(info);
1283 break;
agoode99d63292015-04-13 08:39:25 -07001284 case MidiPort::Type::kOutput:
agoode5ebc4932015-12-01 08:25:12 -08001285 if (CreateAlsaOutputPort(web_port_index, client_id, port_id))
agoode99d63292015-04-13 08:39:25 -07001286 AddOutputPort(info);
1287 break;
1288 }
agoode99d63292015-04-13 08:39:25 -07001289 } else if (!(*old_port)->connected()) {
1290 // Reconnect.
Avi Drissman3528fd02015-12-18 20:11:31 -05001291 uint32_t web_port_index = (*old_port)->web_port_index();
agoode99d63292015-04-13 08:39:25 -07001292 (*old_port)->Update(new_port->path(), new_port->client_id(),
1293 new_port->port_id(), new_port->client_name(),
1294 new_port->port_name(), new_port->manufacturer(),
1295 new_port->version());
1296 switch ((*old_port)->type()) {
1297 case MidiPort::Type::kInput:
1298 if (Subscribe(web_port_index, (*old_port)->client_id(),
1299 (*old_port)->port_id()))
toyoshimec2570a2016-10-21 02:15:27 -07001300 SetInputPortState(web_port_index, PortState::OPENED);
agoode99d63292015-04-13 08:39:25 -07001301 break;
agoode99d63292015-04-13 08:39:25 -07001302 case MidiPort::Type::kOutput:
1303 if (CreateAlsaOutputPort(web_port_index, (*old_port)->client_id(),
1304 (*old_port)->port_id()))
toyoshimec2570a2016-10-21 02:15:27 -07001305 SetOutputPortState(web_port_index, PortState::OPENED);
agoode99d63292015-04-13 08:39:25 -07001306 break;
1307 }
1308 (*old_port)->set_connected(true);
1309 ++it;
1310 } else {
1311 ++it;
1312 }
1313 }
1314}
1315
agoode975043d2015-05-11 00:46:17 -07001316// TODO(agoode): return false on failure.
agoode99d63292015-04-13 08:39:25 -07001317void MidiManagerAlsa::EnumerateAlsaPorts() {
1318 snd_seq_client_info_t* client_info;
1319 snd_seq_client_info_alloca(&client_info);
1320 snd_seq_port_info_t* port_info;
1321 snd_seq_port_info_alloca(&port_info);
1322
1323 // Enumerate clients.
1324 snd_seq_client_info_set_client(client_info, -1);
agoode5a1aa112015-06-21 20:51:00 -07001325 while (!snd_seq_query_next_client(in_client_.get(), client_info)) {
agoode99d63292015-04-13 08:39:25 -07001326 int client_id = snd_seq_client_info_get_client(client_info);
1327 ProcessClientStartEvent(client_id);
1328
1329 // Enumerate ports.
1330 snd_seq_port_info_set_client(port_info, client_id);
1331 snd_seq_port_info_set_port(port_info, -1);
agoode5a1aa112015-06-21 20:51:00 -07001332 while (!snd_seq_query_next_port(in_client_.get(), port_info)) {
agoode99d63292015-04-13 08:39:25 -07001333 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
1334 ProcessPortStartEvent(*addr);
1335 }
1336 }
1337}
1338
agoode975043d2015-05-11 00:46:17 -07001339bool MidiManagerAlsa::EnumerateUdevCards() {
1340 int err;
1341
1342 device::ScopedUdevEnumeratePtr enumerate(
1343 device::udev_enumerate_new(udev_.get()));
1344 if (!enumerate.get()) {
1345 VLOG(1) << "udev_enumerate_new fails";
1346 return false;
1347 }
1348
1349 err = device::udev_enumerate_add_match_subsystem(enumerate.get(),
1350 kUdevSubsystemSound);
1351 if (err) {
1352 VLOG(1) << "udev_enumerate_add_match_subsystem fails: "
brettwf7f870f2015-06-09 11:05:24 -07001353 << base::safe_strerror(-err);
agoode975043d2015-05-11 00:46:17 -07001354 return false;
1355 }
1356
1357 err = device::udev_enumerate_scan_devices(enumerate.get());
1358 if (err) {
brettwf7f870f2015-06-09 11:05:24 -07001359 VLOG(1) << "udev_enumerate_scan_devices fails: "
1360 << base::safe_strerror(-err);
agoode975043d2015-05-11 00:46:17 -07001361 return false;
1362 }
1363
1364 udev_list_entry* list_entry;
1365 auto* devices = device::udev_enumerate_get_list_entry(enumerate.get());
1366 udev_list_entry_foreach(list_entry, devices) {
1367 const char* path = device::udev_list_entry_get_name(list_entry);
1368 device::ScopedUdevDevicePtr dev(
1369 device::udev_device_new_from_syspath(udev_.get(), path));
1370 if (dev.get())
1371 ProcessUdevEvent(dev.get());
1372 }
1373
1374 return true;
1375}
1376
Avi Drissman3528fd02015-12-18 20:11:31 -05001377bool MidiManagerAlsa::CreateAlsaOutputPort(uint32_t port_index,
agoode99d63292015-04-13 08:39:25 -07001378 int client_id,
1379 int port_id) {
1380 // Create the port.
1381 int out_port = snd_seq_create_simple_port(
agoode5a1aa112015-06-21 20:51:00 -07001382 out_client_.get(), NULL, kCreateOutputPortCaps, kCreatePortType);
agoode99d63292015-04-13 08:39:25 -07001383 if (out_port < 0) {
1384 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(out_port);
1385 return false;
1386 }
1387 // Activate port subscription.
1388 snd_seq_port_subscribe_t* subs;
1389 snd_seq_port_subscribe_alloca(&subs);
1390 snd_seq_addr_t sender;
1391 sender.client = out_client_id_;
1392 sender.port = out_port;
1393 snd_seq_port_subscribe_set_sender(subs, &sender);
1394 snd_seq_addr_t dest;
1395 dest.client = client_id;
1396 dest.port = port_id;
1397 snd_seq_port_subscribe_set_dest(subs, &dest);
agoode5a1aa112015-06-21 20:51:00 -07001398 int err = snd_seq_subscribe_port(out_client_.get(), subs);
agoode99d63292015-04-13 08:39:25 -07001399 if (err != 0) {
1400 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
agoode5a1aa112015-06-21 20:51:00 -07001401 snd_seq_delete_simple_port(out_client_.get(), out_port);
agoode99d63292015-04-13 08:39:25 -07001402 return false;
1403 }
1404
1405 // Update our map.
1406 base::AutoLock lock(out_ports_lock_);
1407 out_ports_[port_index] = out_port;
1408 return true;
1409}
1410
Avi Drissman3528fd02015-12-18 20:11:31 -05001411void MidiManagerAlsa::DeleteAlsaOutputPort(uint32_t port_index) {
agoode99d63292015-04-13 08:39:25 -07001412 base::AutoLock lock(out_ports_lock_);
1413 auto it = out_ports_.find(port_index);
1414 if (it == out_ports_.end())
1415 return;
1416
1417 int alsa_port = it->second;
agoode5a1aa112015-06-21 20:51:00 -07001418 snd_seq_delete_simple_port(out_client_.get(), alsa_port);
agoode99d63292015-04-13 08:39:25 -07001419 out_ports_.erase(it);
1420}
1421
Avi Drissman3528fd02015-12-18 20:11:31 -05001422bool MidiManagerAlsa::Subscribe(uint32_t port_index,
1423 int client_id,
1424 int port_id) {
agoode99d63292015-04-13 08:39:25 -07001425 // Activate port subscription.
1426 snd_seq_port_subscribe_t* subs;
1427 snd_seq_port_subscribe_alloca(&subs);
1428 snd_seq_addr_t sender;
1429 sender.client = client_id;
1430 sender.port = port_id;
1431 snd_seq_port_subscribe_set_sender(subs, &sender);
1432 snd_seq_addr_t dest;
1433 dest.client = in_client_id_;
1434 dest.port = in_port_id_;
1435 snd_seq_port_subscribe_set_dest(subs, &dest);
agoode5a1aa112015-06-21 20:51:00 -07001436 int err = snd_seq_subscribe_port(in_client_.get(), subs);
agoode99d63292015-04-13 08:39:25 -07001437 if (err != 0) {
1438 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
1439 return false;
1440 }
1441
1442 // Update our map.
1443 source_map_[AddrToInt(client_id, port_id)] = port_index;
1444 return true;
1445}
1446
toyoshimf4d61522017-02-10 02:03:32 -08001447MidiManager* MidiManager::Create(MidiService* service) {
1448 return new MidiManagerAlsa(service);
toyoshim@chromium.orga97eebf2014-01-03 07:52:39 +00001449}
1450
toyoshime147c5e2015-05-07 21:58:31 -07001451} // namespace midi