Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / media / midi / midi_manager_alsa.cc
bloba6e35e99be96f98cfdac7d47ba1bcf178d1480a6
1 // 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.
5 #include "media/midi/midi_manager_alsa.h"
7 #include <poll.h>
8 #include <stdlib.h>
9 #include <algorithm>
10 #include <string>
12 #include "base/bind.h"
13 #include "base/json/json_string_value_serializer.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/safe_strerror_posix.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/time/time.h"
21 #include "crypto/sha2.h"
22 #include "media/midi/midi_port_info.h"
24 namespace media {
25 namespace midi {
27 namespace {
29 // Per-output buffer. This can be smaller, but then large sysex messages
30 // will be (harmlessly) split across multiple seq events. This should
31 // not have any real practical effect, except perhaps to slightly reorder
32 // realtime messages with respect to sysex.
33 const size_t kSendBufferSize = 256;
35 // Minimum client id for which we will have ALSA card devices for. When we
36 // are searching for card devices (used to get the path, id, and manufacturer),
37 // we don't want to get confused by kernel clients that do not have a card.
38 // See seq_clientmgr.c in the ALSA code for this.
39 // TODO(agoode): Add proper client -> card export from the kernel to avoid
40 // hardcoding.
41 const int kMinimumClientIdForCards = 16;
43 // ALSA constants.
44 const char kAlsaHw[] = "hw";
46 // udev constants.
47 const char kUdev[] = "udev";
48 const char kUdevSubsystemSound[] = "sound";
49 const char kUdevPropertySoundInitialized[] = "SOUND_INITIALIZED";
50 const char kUdevActionChange[] = "change";
51 const char kUdevActionRemove[] = "remove";
53 const char kUdevIdVendor[] = "ID_VENDOR";
54 const char kUdevIdVendorEnc[] = "ID_VENDOR_ENC";
55 const char kUdevIdVendorFromDatabase[] = "ID_VENDOR_FROM_DATABASE";
56 const char kUdevIdVendorId[] = "ID_VENDOR_ID";
57 const char kUdevIdModelId[] = "ID_MODEL_ID";
58 const char kUdevIdBus[] = "ID_BUS";
59 const char kUdevIdPath[] = "ID_PATH";
60 const char kUdevIdUsbInterfaceNum[] = "ID_USB_INTERFACE_NUM";
61 const char kUdevIdSerialShort[] = "ID_SERIAL_SHORT";
63 const char kSysattrVendorName[] = "vendor_name";
64 const char kSysattrVendor[] = "vendor";
65 const char kSysattrModel[] = "model";
66 const char kSysattrGuid[] = "guid";
68 const char kCardSyspath[] = "/card";
70 // Constants for the capabilities we search for in inputs and outputs.
71 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
72 const unsigned int kRequiredInputPortCaps =
73 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
74 const unsigned int kRequiredOutputPortCaps =
75 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
77 const unsigned int kCreateOutputPortCaps =
78 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT;
79 const unsigned int kCreateInputPortCaps =
80 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT;
81 const unsigned int kCreatePortType =
82 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
84 int AddrToInt(int client, int port) {
85 return (client << 8) | port;
88 // Returns true if this client has an ALSA card associated with it.
89 bool IsCardClient(snd_seq_client_type_t type, int client_id) {
90 return (type == SND_SEQ_KERNEL_CLIENT) &&
91 (client_id >= kMinimumClientIdForCards);
94 // TODO(agoode): Move this to device/udev_linux.
95 const std::string UdevDeviceGetPropertyOrSysattr(
96 struct udev_device* udev_device,
97 const char* property_key,
98 const char* sysattr_key) {
99 // First try the property.
100 std::string value =
101 device::UdevDeviceGetPropertyValue(udev_device, property_key);
103 // If no property, look for sysattrs and walk up the parent devices too.
104 while (value.empty() && udev_device) {
105 value = device::UdevDeviceGetSysattrValue(udev_device, sysattr_key);
106 udev_device = device::udev_device_get_parent(udev_device);
108 return value;
111 int GetCardNumber(udev_device* dev) {
112 const char* syspath = device::udev_device_get_syspath(dev);
113 if (!syspath)
114 return -1;
116 std::string syspath_str(syspath);
117 size_t i = syspath_str.rfind(kCardSyspath);
118 if (i == std::string::npos)
119 return -1;
121 int number;
122 if (!base::StringToInt(syspath_str.substr(i + strlen(kCardSyspath)), &number))
123 return -1;
124 return number;
127 std::string GetVendor(udev_device* dev) {
128 // Try to get the vendor string. Sometimes it is encoded.
129 std::string vendor = device::UdevDecodeString(
130 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorEnc));
131 // Sometimes it is not encoded.
132 if (vendor.empty())
133 vendor =
134 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendor, kSysattrVendorName);
135 return vendor;
138 void SetStringIfNonEmpty(base::DictionaryValue* value,
139 const std::string& path,
140 const std::string& in_value) {
141 if (!in_value.empty())
142 value->SetString(path, in_value);
145 } // namespace
147 MidiManagerAlsa::MidiManagerAlsa()
148 : in_client_(NULL),
149 out_client_(NULL),
150 out_client_id_(-1),
151 in_port_id_(-1),
152 alsa_cards_deleter_(&alsa_cards_),
153 alsa_card_midi_count_(0),
154 decoder_(NULL),
155 udev_(device::udev_new()),
156 send_thread_("MidiSendThread"),
157 event_thread_("MidiEventThread"),
158 event_thread_shutdown_(false) {
159 // Initialize decoder.
160 snd_midi_event_new(0, &decoder_);
161 snd_midi_event_no_status(decoder_, 1);
164 MidiManagerAlsa::~MidiManagerAlsa() {
165 // Tell the event thread it will soon be time to shut down. This gives
166 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
167 // message is lost.
169 base::AutoLock lock(shutdown_lock_);
170 event_thread_shutdown_ = true;
173 // Stop the send thread.
174 send_thread_.Stop();
176 // Close the out client. This will trigger the event thread to stop,
177 // because of SND_SEQ_EVENT_CLIENT_EXIT.
178 if (out_client_)
179 snd_seq_close(out_client_);
181 // Wait for the event thread to stop.
182 event_thread_.Stop();
184 // Close the in client.
185 if (in_client_)
186 snd_seq_close(in_client_);
188 // Free the decoder.
189 snd_midi_event_free(decoder_);
192 void MidiManagerAlsa::StartInitialization() {
193 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
195 // Create client handles.
196 int err =
197 snd_seq_open(&in_client_, kAlsaHw, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
198 if (err != 0) {
199 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
200 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
202 in_client_id_ = snd_seq_client_id(in_client_);
203 err = snd_seq_open(&out_client_, kAlsaHw, SND_SEQ_OPEN_OUTPUT, 0);
204 if (err != 0) {
205 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
206 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
208 out_client_id_ = snd_seq_client_id(out_client_);
210 // Name the clients.
211 err = snd_seq_set_client_name(in_client_, "Chrome (input)");
212 if (err != 0) {
213 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
214 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
216 err = snd_seq_set_client_name(out_client_, "Chrome (output)");
217 if (err != 0) {
218 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
219 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
222 // Create input port.
223 in_port_id_ = snd_seq_create_simple_port(
224 in_client_, NULL, kCreateInputPortCaps, kCreatePortType);
225 if (in_port_id_ < 0) {
226 VLOG(1) << "snd_seq_create_simple_port fails: "
227 << snd_strerror(in_port_id_);
228 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
231 // Subscribe to the announce port.
232 snd_seq_port_subscribe_t* subs;
233 snd_seq_port_subscribe_alloca(&subs);
234 snd_seq_addr_t announce_sender;
235 snd_seq_addr_t announce_dest;
236 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
237 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
238 announce_dest.client = in_client_id_;
239 announce_dest.port = in_port_id_;
240 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
241 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
242 err = snd_seq_subscribe_port(in_client_, subs);
243 if (err != 0) {
244 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
245 << snd_strerror(err);
246 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
249 // Generate hotplug events for existing ports.
250 // TODO(agoode): Check the return value for failure.
251 EnumerateAlsaPorts();
253 // Initialize udev monitor.
254 udev_monitor_.reset(
255 device::udev_monitor_new_from_netlink(udev_.get(), kUdev));
256 if (!udev_monitor_.get()) {
257 VLOG(1) << "udev_monitor_new_from_netlink fails";
258 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
260 err = device::udev_monitor_filter_add_match_subsystem_devtype(
261 udev_monitor_.get(), kUdevSubsystemSound, nullptr);
262 if (err != 0) {
263 VLOG(1) << "udev_monitor_add_match_subsystem fails: "
264 << safe_strerror(-err);
265 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
267 err = device::udev_monitor_enable_receiving(udev_monitor_.get());
268 if (err != 0) {
269 VLOG(1) << "udev_monitor_enable_receiving fails: " << safe_strerror(-err);
270 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
273 // Generate hotplug events for existing udev devices.
274 EnumerateUdevCards();
276 // Start processing events.
277 event_thread_.Start();
278 event_thread_.message_loop()->PostTask(
279 FROM_HERE,
280 base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this)));
282 CompleteInitialization(MIDI_OK);
285 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
286 uint32 port_index,
287 const std::vector<uint8>& data,
288 double timestamp) {
289 // Not correct right now. http://crbug.com/374341.
290 if (!send_thread_.IsRunning())
291 send_thread_.Start();
293 base::TimeDelta delay;
294 if (timestamp != 0.0) {
295 base::TimeTicks time_to_send =
296 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
297 timestamp * base::Time::kMicrosecondsPerSecond);
298 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
301 send_thread_.message_loop()->PostDelayedTask(
302 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData,
303 base::Unretained(this), port_index, data),
304 delay);
306 // Acknowledge send.
307 send_thread_.message_loop()->PostTask(
308 FROM_HERE, base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
309 base::Unretained(client), data.size()));
312 MidiManagerAlsa::MidiPort::Id::Id() = default;
314 MidiManagerAlsa::MidiPort::Id::Id(const std::string& bus,
315 const std::string& vendor_id,
316 const std::string& model_id,
317 const std::string& usb_interface_num,
318 const std::string& serial)
319 : bus_(bus),
320 vendor_id_(vendor_id),
321 model_id_(model_id),
322 usb_interface_num_(usb_interface_num),
323 serial_(serial) {
326 MidiManagerAlsa::MidiPort::Id::Id(const Id&) = default;
328 MidiManagerAlsa::MidiPort::Id::~Id() = default;
330 bool MidiManagerAlsa::MidiPort::Id::operator==(const Id& rhs) const {
331 return (bus_ == rhs.bus_) && (vendor_id_ == rhs.vendor_id_) &&
332 (model_id_ == rhs.model_id_) &&
333 (usb_interface_num_ == rhs.usb_interface_num_) &&
334 (serial_ == rhs.serial_);
337 bool MidiManagerAlsa::MidiPort::Id::empty() const {
338 return bus_.empty() && vendor_id_.empty() && model_id_.empty() &&
339 usb_interface_num_.empty() && serial_.empty();
342 MidiManagerAlsa::MidiPort::MidiPort(const std::string& path,
343 const Id& id,
344 int client_id,
345 int port_id,
346 int midi_device,
347 const std::string& client_name,
348 const std::string& port_name,
349 const std::string& manufacturer,
350 const std::string& version,
351 Type type)
352 : id_(id),
353 midi_device_(midi_device),
354 type_(type),
355 path_(path),
356 client_id_(client_id),
357 port_id_(port_id),
358 client_name_(client_name),
359 port_name_(port_name),
360 manufacturer_(manufacturer),
361 version_(version),
362 web_port_index_(0),
363 connected_(true) {
366 MidiManagerAlsa::MidiPort::~MidiPort() = default;
368 // Note: keep synchronized with the MidiPort::Match* methods.
369 scoped_ptr<base::Value> MidiManagerAlsa::MidiPort::Value() const {
370 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue);
372 std::string type;
373 switch (type_) {
374 case Type::kInput:
375 type = "input";
376 break;
377 case Type::kOutput:
378 type = "output";
379 break;
381 value->SetString("type", type);
382 SetStringIfNonEmpty(value.get(), "path", path_);
383 SetStringIfNonEmpty(value.get(), "clientName", client_name_);
384 SetStringIfNonEmpty(value.get(), "portName", port_name_);
385 value->SetInteger("clientId", client_id_);
386 value->SetInteger("portId", port_id_);
387 value->SetInteger("midiDevice", midi_device_);
389 // Flatten id fields.
390 SetStringIfNonEmpty(value.get(), "bus", id_.bus());
391 SetStringIfNonEmpty(value.get(), "vendorId", id_.vendor_id());
392 SetStringIfNonEmpty(value.get(), "modelId", id_.model_id());
393 SetStringIfNonEmpty(value.get(), "usbInterfaceNum", id_.usb_interface_num());
394 SetStringIfNonEmpty(value.get(), "serial", id_.serial());
396 return value.Pass();
399 std::string MidiManagerAlsa::MidiPort::JSONValue() const {
400 std::string json;
401 JSONStringValueSerializer serializer(&json);
402 serializer.Serialize(*Value().get());
403 return json;
406 // TODO(agoode): Do not use SHA256 here. Instead store a persistent
407 // mapping and just use a UUID or other random string.
408 // http://crbug.com/465320
409 std::string MidiManagerAlsa::MidiPort::OpaqueKey() const {
410 uint8 hash[crypto::kSHA256Length];
411 crypto::SHA256HashString(JSONValue(), &hash, sizeof(hash));
412 return base::HexEncode(&hash, sizeof(hash));
415 bool MidiManagerAlsa::MidiPort::MatchConnected(const MidiPort& query) const {
416 // Matches on:
417 // connected == true
418 // type
419 // path
420 // id
421 // client_id
422 // port_id
423 // midi_device
424 // client_name
425 // port_name
426 return connected() && (type() == query.type()) && (path() == query.path()) &&
427 (id() == query.id()) && (client_id() == query.client_id()) &&
428 (port_id() == query.port_id()) &&
429 (midi_device() == query.midi_device()) &&
430 (client_name() == query.client_name()) &&
431 (port_name() == query.port_name());
434 bool MidiManagerAlsa::MidiPort::MatchCardPass1(const MidiPort& query) const {
435 // Matches on:
436 // connected == false
437 // type
438 // path
439 // id
440 // port_id
441 // midi_device
442 return MatchCardPass2(query) && (path() == query.path());
445 bool MidiManagerAlsa::MidiPort::MatchCardPass2(const MidiPort& query) const {
446 // Matches on:
447 // connected == false
448 // type
449 // id
450 // port_id
451 // midi_device
452 return !connected() && (type() == query.type()) && (id() == query.id()) &&
453 (port_id() == query.port_id()) &&
454 (midi_device() == query.midi_device());
457 bool MidiManagerAlsa::MidiPort::MatchNoCardPass1(const MidiPort& query) const {
458 // Matches on:
459 // connected == false
460 // type
461 // path.empty(), for both this and query
462 // id.empty(), for both this and query
463 // client_id
464 // port_id
465 // client_name
466 // port_name
467 // midi_device == -1, for both this and query
468 return MatchNoCardPass2(query) && (client_id() == query.client_id());
471 bool MidiManagerAlsa::MidiPort::MatchNoCardPass2(const MidiPort& query) const {
472 // Matches on:
473 // connected == false
474 // type
475 // path.empty(), for both this and query
476 // id.empty(), for both this and query
477 // port_id
478 // client_name
479 // port_name
480 // midi_device == -1, for both this and query
481 return !connected() && (type() == query.type()) && path().empty() &&
482 query.path().empty() && id().empty() && query.id().empty() &&
483 (port_id() == query.port_id()) &&
484 (client_name() == query.client_name()) &&
485 (port_name() == query.port_name()) && (midi_device() == -1) &&
486 (query.midi_device() == -1);
489 MidiManagerAlsa::MidiPortStateBase::~MidiPortStateBase() = default;
491 MidiManagerAlsa::MidiPortStateBase::iterator
492 MidiManagerAlsa::MidiPortStateBase::Find(
493 const MidiManagerAlsa::MidiPort& port) {
494 auto result = FindConnected(port);
495 if (result == end())
496 result = FindDisconnected(port);
497 return result;
500 MidiManagerAlsa::MidiPortStateBase::iterator
501 MidiManagerAlsa::MidiPortStateBase::FindConnected(
502 const MidiManagerAlsa::MidiPort& port) {
503 // Exact match required for connected ports.
504 auto it = std::find_if(ports_.begin(), ports_.end(), [&port](MidiPort* p) {
505 return p->MatchConnected(port);
507 return it;
510 MidiManagerAlsa::MidiPortStateBase::iterator
511 MidiManagerAlsa::MidiPortStateBase::FindDisconnected(
512 const MidiManagerAlsa::MidiPort& port) {
513 // Always match on:
514 // type
515 // Possible things to match on:
516 // path
517 // id
518 // client_id
519 // port_id
520 // midi_device
521 // client_name
522 // port_name
524 if (!port.path().empty()) {
525 // If path is present, then we have a card-based client.
527 // Pass 1. Match on path, id, midi_device, port_id.
528 // This is the best possible match for hardware card-based clients.
529 // This will also match the empty id correctly for devices without an id.
530 auto it = std::find_if(ports_.begin(), ports_.end(), [&port](MidiPort* p) {
531 return p->MatchCardPass1(port);
533 if (it != ports_.end())
534 return it;
536 if (!port.id().empty()) {
537 // Pass 2. Match on id, midi_device, port_id.
538 // This will give us a high-confidence match when a user moves a device to
539 // another USB/Firewire/Thunderbolt/etc port, but only works if the device
540 // has a hardware id.
541 it = std::find_if(ports_.begin(), ports_.end(), [&port](MidiPort* p) {
542 return p->MatchCardPass2(port);
544 if (it != ports_.end())
545 return it;
547 } else {
548 // Else, we have a non-card-based client.
549 // Pass 1. Match on client_id, port_id, client_name, port_name.
550 // This will give us a reasonably good match.
551 auto it = std::find_if(ports_.begin(), ports_.end(), [&port](MidiPort* p) {
552 return p->MatchNoCardPass1(port);
554 if (it != ports_.end())
555 return it;
557 // Pass 2. Match on port_id, client_name, port_name.
558 // This is weaker but similar to pass 2 in the hardware card-based clients
559 // match.
560 it = std::find_if(ports_.begin(), ports_.end(), [&port](MidiPort* p) {
561 return p->MatchNoCardPass2(port);
563 if (it != ports_.end())
564 return it;
567 // No match.
568 return ports_.end();
571 MidiManagerAlsa::MidiPortStateBase::MidiPortStateBase() = default;
573 void MidiManagerAlsa::TemporaryMidiPortState::Insert(
574 scoped_ptr<MidiPort> port) {
575 ports()->push_back(port.Pass());
578 MidiManagerAlsa::MidiPortState::MidiPortState()
579 : num_input_ports_(0), num_output_ports_(0) {
582 uint32 MidiManagerAlsa::MidiPortState::Insert(scoped_ptr<MidiPort> port) {
583 // Add the web midi index.
584 uint32 web_port_index = 0;
585 switch (port->type()) {
586 case MidiPort::Type::kInput:
587 web_port_index = num_input_ports_++;
588 break;
589 case MidiPort::Type::kOutput:
590 web_port_index = num_output_ports_++;
591 break;
593 port->set_web_port_index(web_port_index);
594 ports()->push_back(port.Pass());
595 return web_port_index;
598 MidiManagerAlsa::AlsaSeqState::AlsaSeqState()
599 : clients_deleter_(&clients_), card_client_count_(0) {
602 MidiManagerAlsa::AlsaSeqState::~AlsaSeqState() = default;
604 void MidiManagerAlsa::AlsaSeqState::ClientStart(int client_id,
605 const std::string& client_name,
606 snd_seq_client_type_t type) {
607 ClientExit(client_id);
608 clients_[client_id] = new Client(client_name, type);
609 if (IsCardClient(type, client_id))
610 ++card_client_count_;
613 bool MidiManagerAlsa::AlsaSeqState::ClientStarted(int client_id) {
614 return clients_.find(client_id) != clients_.end();
617 void MidiManagerAlsa::AlsaSeqState::ClientExit(int client_id) {
618 auto it = clients_.find(client_id);
619 if (it != clients_.end()) {
620 if (IsCardClient(it->second->type(), client_id))
621 --card_client_count_;
622 delete it->second;
623 clients_.erase(it);
627 void MidiManagerAlsa::AlsaSeqState::PortStart(
628 int client_id,
629 int port_id,
630 const std::string& port_name,
631 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
632 bool midi) {
633 auto it = clients_.find(client_id);
634 if (it != clients_.end())
635 it->second->AddPort(port_id,
636 scoped_ptr<Port>(new Port(port_name, direction, midi)));
639 void MidiManagerAlsa::AlsaSeqState::PortExit(int client_id, int port_id) {
640 auto it = clients_.find(client_id);
641 if (it != clients_.end())
642 it->second->RemovePort(port_id);
645 snd_seq_client_type_t MidiManagerAlsa::AlsaSeqState::ClientType(
646 int client_id) const {
647 auto it = clients_.find(client_id);
648 if (it == clients_.end())
649 return SND_SEQ_USER_CLIENT;
650 return it->second->type();
653 scoped_ptr<MidiManagerAlsa::TemporaryMidiPortState>
654 MidiManagerAlsa::AlsaSeqState::ToMidiPortState(const AlsaCardMap& alsa_cards) {
655 scoped_ptr<MidiManagerAlsa::TemporaryMidiPortState> midi_ports(
656 new TemporaryMidiPortState);
657 // TODO(agoode): Use more information from udev, to allow hardware matching.
658 // See http://crbug.com/486471.
659 auto card_it = alsa_cards.begin();
661 int card_midi_device = -1;
662 for (const auto& client_pair : clients_) {
663 int client_id = client_pair.first;
664 const auto& client = client_pair.second;
666 // Get client metadata.
667 const std::string client_name = client->name();
668 std::string manufacturer;
669 std::string driver;
670 std::string path;
671 MidiPort::Id id;
672 std::string card_name;
673 std::string card_longname;
674 int midi_device = -1;
676 if (IsCardClient(client->type(), client_id)) {
677 auto& card = card_it->second;
678 if (card_midi_device == -1)
679 card_midi_device = 0;
681 manufacturer = card->manufacturer();
682 path = card->path();
683 id = MidiPort::Id(card->bus(), card->vendor_id(), card->model_id(),
684 card->usb_interface_num(), card->serial());
685 card_name = card->name();
686 card_longname = card->longname();
687 midi_device = card_midi_device;
689 ++card_midi_device;
690 if (card_midi_device >= card->midi_device_count()) {
691 card_midi_device = -1;
692 ++card_it;
696 for (const auto& port_pair : *client) {
697 int port_id = port_pair.first;
698 const auto& port = port_pair.second;
700 if (port->midi()) {
701 std::string version;
702 if (!driver.empty()) {
703 version = driver + " / ";
705 version +=
706 base::StringPrintf("ALSA library version %d.%d.%d", SND_LIB_MAJOR,
707 SND_LIB_MINOR, SND_LIB_SUBMINOR);
708 PortDirection direction = port->direction();
709 if (direction == PortDirection::kInput ||
710 direction == PortDirection::kDuplex) {
711 midi_ports->Insert(scoped_ptr<MidiPort>(new MidiPort(
712 path, id, client_id, port_id, midi_device, client->name(),
713 port->name(), manufacturer, version, MidiPort::Type::kInput)));
715 if (direction == PortDirection::kOutput ||
716 direction == PortDirection::kDuplex) {
717 midi_ports->Insert(scoped_ptr<MidiPort>(new MidiPort(
718 path, id, client_id, port_id, midi_device, client->name(),
719 port->name(), manufacturer, version, MidiPort::Type::kOutput)));
725 return midi_ports.Pass();
728 MidiManagerAlsa::AlsaSeqState::Port::Port(
729 const std::string& name,
730 MidiManagerAlsa::AlsaSeqState::PortDirection direction,
731 bool midi)
732 : name_(name), direction_(direction), midi_(midi) {
735 MidiManagerAlsa::AlsaSeqState::Port::~Port() = default;
737 MidiManagerAlsa::AlsaSeqState::Client::Client(const std::string& name,
738 snd_seq_client_type_t type)
739 : name_(name), type_(type), ports_deleter_(&ports_) {
742 MidiManagerAlsa::AlsaSeqState::Client::~Client() = default;
744 void MidiManagerAlsa::AlsaSeqState::Client::AddPort(int addr,
745 scoped_ptr<Port> port) {
746 RemovePort(addr);
747 ports_[addr] = port.release();
750 void MidiManagerAlsa::AlsaSeqState::Client::RemovePort(int addr) {
751 auto it = ports_.find(addr);
752 if (it != ports_.end()) {
753 delete it->second;
754 ports_.erase(it);
758 MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
759 MidiManagerAlsa::AlsaSeqState::Client::begin() const {
760 return ports_.begin();
763 MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator
764 MidiManagerAlsa::AlsaSeqState::Client::end() const {
765 return ports_.end();
768 MidiManagerAlsa::AlsaCard::AlsaCard(udev_device* dev,
769 const std::string& name,
770 const std::string& longname,
771 const std::string& driver,
772 int midi_device_count)
773 : name_(name),
774 longname_(longname),
775 driver_(driver),
776 path_(device::UdevDeviceGetPropertyValue(dev, kUdevIdPath)),
777 bus_(device::UdevDeviceGetPropertyValue(dev, kUdevIdBus)),
778 vendor_id_(
779 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendorId, kSysattrVendor)),
780 model_id_(
781 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdModelId, kSysattrModel)),
782 usb_interface_num_(
783 device::UdevDeviceGetPropertyValue(dev, kUdevIdUsbInterfaceNum)),
784 serial_(UdevDeviceGetPropertyOrSysattr(dev,
785 kUdevIdSerialShort,
786 kSysattrGuid)),
787 midi_device_count_(midi_device_count),
788 manufacturer_(ExtractManufacturerString(
789 GetVendor(dev),
790 vendor_id_,
791 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorFromDatabase),
792 name,
793 longname)) {
796 MidiManagerAlsa::AlsaCard::~AlsaCard() = default;
798 // static
799 std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
800 const std::string& udev_id_vendor,
801 const std::string& udev_id_vendor_id,
802 const std::string& udev_id_vendor_from_database,
803 const std::string& alsa_name,
804 const std::string& alsa_longname) {
805 // Let's try to determine the manufacturer. Here is the ordered preference
806 // in extraction:
807 // 1. Vendor name from the hardware device string, from udev properties
808 // or sysattrs.
809 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
810 // 3. Heuristic from ALSA.
812 // Is the vendor string present and not just the vendor hex id?
813 if (!udev_id_vendor.empty() && (udev_id_vendor != udev_id_vendor_id)) {
814 return udev_id_vendor;
817 // Is there a vendor string in the hardware database?
818 if (!udev_id_vendor_from_database.empty()) {
819 return udev_id_vendor_from_database;
822 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
823 // We assume that card longname is in the format of
824 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
825 // a manufacturer name here.
826 size_t at_index = alsa_longname.rfind(" at ");
827 if (at_index && at_index != std::string::npos) {
828 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
829 if (name_index && name_index != std::string::npos)
830 return alsa_longname.substr(0, name_index - 1);
833 // Failure.
834 return "";
837 void MidiManagerAlsa::SendMidiData(uint32 port_index,
838 const std::vector<uint8>& data) {
839 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
841 snd_midi_event_t* encoder;
842 snd_midi_event_new(kSendBufferSize, &encoder);
843 for (unsigned int i = 0; i < data.size(); i++) {
844 snd_seq_event_t event;
845 int result = snd_midi_event_encode_byte(encoder, data[i], &event);
846 if (result == 1) {
847 // Full event, send it.
848 base::AutoLock lock(out_ports_lock_);
849 auto it = out_ports_.find(port_index);
850 if (it != out_ports_.end()) {
851 snd_seq_ev_set_source(&event, it->second);
852 snd_seq_ev_set_subs(&event);
853 snd_seq_ev_set_direct(&event);
854 snd_seq_event_output_direct(out_client_, &event);
858 snd_midi_event_free(encoder);
861 void MidiManagerAlsa::ScheduleEventLoop() {
862 event_thread_.message_loop()->PostTask(
863 FROM_HERE,
864 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
867 void MidiManagerAlsa::EventLoop() {
868 bool loop_again = true;
870 struct pollfd pfd[2];
871 snd_seq_poll_descriptors(in_client_, &pfd[0], 1, POLLIN);
872 pfd[1].fd = device::udev_monitor_get_fd(udev_monitor_.get());
873 pfd[1].events = POLLIN;
875 int err = HANDLE_EINTR(poll(pfd, arraysize(pfd), -1));
876 if (err < 0) {
877 VLOG(1) << "poll fails: " << safe_strerror(errno);
878 loop_again = false;
879 } else {
880 if (pfd[0].revents & POLLIN) {
881 // Read available incoming MIDI data.
882 int remaining;
883 double timestamp =
884 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
885 do {
886 snd_seq_event_t* event;
887 err = snd_seq_event_input(in_client_, &event);
888 remaining = snd_seq_event_input_pending(in_client_, 0);
890 if (err == -ENOSPC) {
891 // Handle out of space error.
892 VLOG(1) << "snd_seq_event_input detected buffer overrun";
893 // We've lost events: check another way to see if we need to shut
894 // down.
895 base::AutoLock lock(shutdown_lock_);
896 if (event_thread_shutdown_)
897 loop_again = false;
898 } else if (err == -EAGAIN) {
899 // We've read all the data.
900 } else if (err < 0) {
901 // Handle other errors.
902 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
903 // TODO(agoode): Use RecordAction() or similar to log this.
904 loop_again = false;
905 } else if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
906 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
907 // Handle announce events.
908 switch (event->type) {
909 case SND_SEQ_EVENT_PORT_START:
910 // Don't use SND_SEQ_EVENT_CLIENT_START because the
911 // client name may not be set by the time we query
912 // it. It should be set by the time ports are made.
913 ProcessClientStartEvent(event->data.addr.client);
914 ProcessPortStartEvent(event->data.addr);
915 break;
916 case SND_SEQ_EVENT_CLIENT_EXIT:
917 // Check for disconnection of our "out" client. This means "shut
918 // down".
919 if (event->data.addr.client == out_client_id_) {
920 loop_again = false;
921 remaining = 0;
922 } else
923 ProcessClientExitEvent(event->data.addr);
924 break;
925 case SND_SEQ_EVENT_PORT_EXIT:
926 ProcessPortExitEvent(event->data.addr);
927 break;
929 } else {
930 // Normal operation.
931 ProcessSingleEvent(event, timestamp);
933 } while (remaining > 0);
935 if (pfd[1].revents & POLLIN) {
936 device::ScopedUdevDevicePtr dev(
937 device::udev_monitor_receive_device(udev_monitor_.get()));
938 if (dev.get())
939 ProcessUdevEvent(dev.get());
940 else
941 VLOG(1) << "udev_monitor_receive_device fails";
945 // Do again.
946 if (loop_again)
947 ScheduleEventLoop();
950 void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
951 double timestamp) {
952 auto source_it =
953 source_map_.find(AddrToInt(event->source.client, event->source.port));
954 if (source_it != source_map_.end()) {
955 uint32 source = source_it->second;
956 if (event->type == SND_SEQ_EVENT_SYSEX) {
957 // Special! Variable-length sysex.
958 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr),
959 event->data.ext.len, timestamp);
960 } else {
961 // Otherwise, decode this and send that on.
962 unsigned char buf[12];
963 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
964 if (count <= 0) {
965 if (count != -ENOENT) {
966 // ENOENT means that it's not a MIDI message, which is not an
967 // error, but other negative values are errors for us.
968 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
969 // TODO(agoode): Record this failure.
971 } else {
972 ReceiveMidiData(source, buf, count, timestamp);
978 void MidiManagerAlsa::ProcessClientStartEvent(int client_id) {
979 // Ignore if client is already started.
980 if (alsa_seq_state_.ClientStarted(client_id))
981 return;
983 snd_seq_client_info_t* client_info;
984 snd_seq_client_info_alloca(&client_info);
985 int err = snd_seq_get_any_client_info(in_client_, client_id, client_info);
986 if (err != 0)
987 return;
989 // Skip our own clients.
990 if ((client_id == in_client_id_) || (client_id == out_client_id_))
991 return;
993 // Update our view of ALSA seq state.
994 alsa_seq_state_.ClientStart(client_id,
995 snd_seq_client_info_get_name(client_info),
996 snd_seq_client_info_get_type(client_info));
998 // Generate Web MIDI events.
999 UpdatePortStateAndGenerateEvents();
1002 void MidiManagerAlsa::ProcessPortStartEvent(const snd_seq_addr_t& addr) {
1003 snd_seq_port_info_t* port_info;
1004 snd_seq_port_info_alloca(&port_info);
1005 int err =
1006 snd_seq_get_any_port_info(in_client_, addr.client, addr.port, port_info);
1007 if (err != 0)
1008 return;
1010 unsigned int caps = snd_seq_port_info_get_capability(port_info);
1011 bool input = (caps & kRequiredInputPortCaps) == kRequiredInputPortCaps;
1012 bool output = (caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps;
1013 AlsaSeqState::PortDirection direction;
1014 if (input && output)
1015 direction = AlsaSeqState::PortDirection::kDuplex;
1016 else if (input)
1017 direction = AlsaSeqState::PortDirection::kInput;
1018 else if (output)
1019 direction = AlsaSeqState::PortDirection::kOutput;
1020 else
1021 return;
1023 // Update our view of ALSA seq state.
1024 alsa_seq_state_.PortStart(
1025 addr.client, addr.port, snd_seq_port_info_get_name(port_info), direction,
1026 snd_seq_port_info_get_type(port_info) & SND_SEQ_PORT_TYPE_MIDI_GENERIC);
1027 // Generate Web MIDI events.
1028 UpdatePortStateAndGenerateEvents();
1031 void MidiManagerAlsa::ProcessClientExitEvent(const snd_seq_addr_t& addr) {
1032 // Update our view of ALSA seq state.
1033 alsa_seq_state_.ClientExit(addr.client);
1034 // Generate Web MIDI events.
1035 UpdatePortStateAndGenerateEvents();
1038 void MidiManagerAlsa::ProcessPortExitEvent(const snd_seq_addr_t& addr) {
1039 // Update our view of ALSA seq state.
1040 alsa_seq_state_.PortExit(addr.client, addr.port);
1041 // Generate Web MIDI events.
1042 UpdatePortStateAndGenerateEvents();
1045 void MidiManagerAlsa::ProcessUdevEvent(udev_device* dev) {
1046 // Only card devices have this property set, and only when they are
1047 // fully initialized.
1048 if (!device::udev_device_get_property_value(dev,
1049 kUdevPropertySoundInitialized))
1050 return;
1052 // Get the action. If no action, then we are doing first time enumeration
1053 // and the device is treated as new.
1054 const char* action = device::udev_device_get_action(dev);
1055 if (!action)
1056 action = kUdevActionChange;
1058 if (strcmp(action, kUdevActionChange) == 0) {
1059 AddCard(dev);
1060 // Generate Web MIDI events.
1061 UpdatePortStateAndGenerateEvents();
1062 } else if (strcmp(action, kUdevActionRemove) == 0) {
1063 RemoveCard(GetCardNumber(dev));
1064 // Generate Web MIDI events.
1065 UpdatePortStateAndGenerateEvents();
1069 void MidiManagerAlsa::AddCard(udev_device* dev) {
1070 int number = GetCardNumber(dev);
1071 if (number == -1)
1072 return;
1074 RemoveCard(number);
1076 snd_ctl_card_info_t* card;
1077 snd_hwdep_info_t* hwdep;
1078 snd_ctl_card_info_alloca(&card);
1079 snd_hwdep_info_alloca(&hwdep);
1080 const std::string id = base::StringPrintf("hw:CARD=%i", number);
1081 snd_ctl_t* handle;
1082 int err = snd_ctl_open(&handle, id.c_str(), 0);
1083 if (err != 0) {
1084 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
1085 return;
1087 err = snd_ctl_card_info(handle, card);
1088 if (err != 0) {
1089 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
1090 snd_ctl_close(handle);
1091 return;
1093 std::string name = snd_ctl_card_info_get_name(card);
1094 std::string longname = snd_ctl_card_info_get_longname(card);
1095 std::string driver = snd_ctl_card_info_get_driver(card);
1097 // Count rawmidi devices (not subdevices).
1098 int midi_count = 0;
1099 for (int device = -1;
1100 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;)
1101 ++midi_count;
1103 // Count any hwdep synths that become MIDI devices outside of rawmidi.
1105 // Explanation:
1106 // Any kernel driver can create an ALSA client (visible to us).
1107 // With modern hardware, only rawmidi devices do this. Kernel
1108 // drivers create rawmidi devices and the rawmidi subsystem makes
1109 // the seq clients. But the OPL3 driver is special, it does not
1110 // make a rawmidi device but a seq client directly. (This is the
1111 // only one to worry about in the kernel code, as of 2015-03-23.)
1113 // OPL3 is very old (but still possible to get in new
1114 // hardware). It is unlikely that new drivers would not use
1115 // rawmidi and defeat our heuristic.
1117 // Longer term, support should be added in the kernel to expose a
1118 // direct link from card->client (or client->card) so that all
1119 // these heuristics will be obsolete. Once that is there, we can
1120 // assume our old heuristics will work on old kernels and the new
1121 // robust code will be used on new. Then we will not need to worry
1122 // about changes to kernel internals breaking our code.
1123 // See the TODO above at kMinimumClientIdForCards.
1124 for (int device = -1;
1125 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) {
1126 err = snd_ctl_hwdep_info(handle, hwdep);
1127 if (err != 0) {
1128 VLOG(1) << "snd_ctl_hwdep_info fails: " << snd_strerror(err);
1129 continue;
1131 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep);
1132 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 ||
1133 iface == SND_HWDEP_IFACE_OPL4)
1134 ++midi_count;
1136 snd_ctl_close(handle);
1138 if (midi_count > 0)
1139 alsa_cards_[number] = new AlsaCard(dev, name, longname, driver, midi_count);
1140 alsa_card_midi_count_ += midi_count;
1143 void MidiManagerAlsa::RemoveCard(int number) {
1144 auto it = alsa_cards_.find(number);
1145 if (it == alsa_cards_.end())
1146 return;
1148 alsa_card_midi_count_ -= it->second->midi_device_count();
1149 delete it->second;
1150 alsa_cards_.erase(it);
1153 void MidiManagerAlsa::UpdatePortStateAndGenerateEvents() {
1154 // Verify that our information from ALSA and udev are in sync. If
1155 // not, we cannot generate events right now.
1156 if (alsa_card_midi_count_ != alsa_seq_state_.card_client_count())
1157 return;
1159 // Generate new port state.
1160 auto new_port_state = alsa_seq_state_.ToMidiPortState(alsa_cards_);
1162 // Disconnect any connected old ports that are now missing.
1163 for (auto* old_port : port_state_) {
1164 if (old_port->connected() &&
1165 (new_port_state->FindConnected(*old_port) == new_port_state->end())) {
1166 old_port->set_connected(false);
1167 uint32 web_port_index = old_port->web_port_index();
1168 switch (old_port->type()) {
1169 case MidiPort::Type::kInput:
1170 source_map_.erase(
1171 AddrToInt(old_port->client_id(), old_port->port_id()));
1172 SetInputPortState(web_port_index, MIDI_PORT_DISCONNECTED);
1173 break;
1174 case MidiPort::Type::kOutput:
1175 DeleteAlsaOutputPort(web_port_index);
1176 SetOutputPortState(web_port_index, MIDI_PORT_DISCONNECTED);
1177 break;
1182 // Reconnect or add new ports.
1183 auto it = new_port_state->begin();
1184 while (it != new_port_state->end()) {
1185 auto* new_port = *it;
1186 auto old_port = port_state_.Find(*new_port);
1187 if (old_port == port_state_.end()) {
1188 // Add new port.
1189 uint32 web_port_index =
1190 port_state_.Insert(scoped_ptr<MidiPort>(new_port));
1191 MidiPortInfo info(new_port->OpaqueKey(), new_port->manufacturer(),
1192 new_port->port_name(), new_port->version(),
1193 MIDI_PORT_OPENED);
1194 switch (new_port->type()) {
1195 case MidiPort::Type::kInput:
1196 if (Subscribe(web_port_index, new_port->client_id(),
1197 new_port->port_id()))
1198 AddInputPort(info);
1199 break;
1200 case MidiPort::Type::kOutput:
1201 if (CreateAlsaOutputPort(web_port_index, new_port->client_id(),
1202 new_port->port_id()))
1203 AddOutputPort(info);
1204 break;
1206 it = new_port_state->weak_erase(it);
1207 } else if (!(*old_port)->connected()) {
1208 // Reconnect.
1209 uint32 web_port_index = (*old_port)->web_port_index();
1210 (*old_port)->Update(new_port->path(), new_port->client_id(),
1211 new_port->port_id(), new_port->client_name(),
1212 new_port->port_name(), new_port->manufacturer(),
1213 new_port->version());
1214 switch ((*old_port)->type()) {
1215 case MidiPort::Type::kInput:
1216 if (Subscribe(web_port_index, (*old_port)->client_id(),
1217 (*old_port)->port_id()))
1218 SetInputPortState(web_port_index, MIDI_PORT_OPENED);
1219 break;
1220 case MidiPort::Type::kOutput:
1221 if (CreateAlsaOutputPort(web_port_index, (*old_port)->client_id(),
1222 (*old_port)->port_id()))
1223 SetOutputPortState(web_port_index, MIDI_PORT_OPENED);
1224 break;
1226 (*old_port)->set_connected(true);
1227 ++it;
1228 } else {
1229 ++it;
1234 // TODO(agoode): return false on failure.
1235 void MidiManagerAlsa::EnumerateAlsaPorts() {
1236 snd_seq_client_info_t* client_info;
1237 snd_seq_client_info_alloca(&client_info);
1238 snd_seq_port_info_t* port_info;
1239 snd_seq_port_info_alloca(&port_info);
1241 // Enumerate clients.
1242 snd_seq_client_info_set_client(client_info, -1);
1243 while (!snd_seq_query_next_client(in_client_, client_info)) {
1244 int client_id = snd_seq_client_info_get_client(client_info);
1245 ProcessClientStartEvent(client_id);
1247 // Enumerate ports.
1248 snd_seq_port_info_set_client(port_info, client_id);
1249 snd_seq_port_info_set_port(port_info, -1);
1250 while (!snd_seq_query_next_port(in_client_, port_info)) {
1251 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
1252 ProcessPortStartEvent(*addr);
1257 bool MidiManagerAlsa::EnumerateUdevCards() {
1258 int err;
1260 device::ScopedUdevEnumeratePtr enumerate(
1261 device::udev_enumerate_new(udev_.get()));
1262 if (!enumerate.get()) {
1263 VLOG(1) << "udev_enumerate_new fails";
1264 return false;
1267 err = device::udev_enumerate_add_match_subsystem(enumerate.get(),
1268 kUdevSubsystemSound);
1269 if (err) {
1270 VLOG(1) << "udev_enumerate_add_match_subsystem fails: "
1271 << safe_strerror(-err);
1272 return false;
1275 err = device::udev_enumerate_scan_devices(enumerate.get());
1276 if (err) {
1277 VLOG(1) << "udev_enumerate_scan_devices fails: " << safe_strerror(-err);
1278 return false;
1281 udev_list_entry* list_entry;
1282 auto* devices = device::udev_enumerate_get_list_entry(enumerate.get());
1283 udev_list_entry_foreach(list_entry, devices) {
1284 const char* path = device::udev_list_entry_get_name(list_entry);
1285 device::ScopedUdevDevicePtr dev(
1286 device::udev_device_new_from_syspath(udev_.get(), path));
1287 if (dev.get())
1288 ProcessUdevEvent(dev.get());
1291 return true;
1294 bool MidiManagerAlsa::CreateAlsaOutputPort(uint32 port_index,
1295 int client_id,
1296 int port_id) {
1297 // Create the port.
1298 int out_port = snd_seq_create_simple_port(
1299 out_client_, NULL, kCreateOutputPortCaps, kCreatePortType);
1300 if (out_port < 0) {
1301 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(out_port);
1302 return false;
1304 // Activate port subscription.
1305 snd_seq_port_subscribe_t* subs;
1306 snd_seq_port_subscribe_alloca(&subs);
1307 snd_seq_addr_t sender;
1308 sender.client = out_client_id_;
1309 sender.port = out_port;
1310 snd_seq_port_subscribe_set_sender(subs, &sender);
1311 snd_seq_addr_t dest;
1312 dest.client = client_id;
1313 dest.port = port_id;
1314 snd_seq_port_subscribe_set_dest(subs, &dest);
1315 int err = snd_seq_subscribe_port(out_client_, subs);
1316 if (err != 0) {
1317 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
1318 snd_seq_delete_simple_port(out_client_, out_port);
1319 return false;
1322 // Update our map.
1323 base::AutoLock lock(out_ports_lock_);
1324 out_ports_[port_index] = out_port;
1325 return true;
1328 void MidiManagerAlsa::DeleteAlsaOutputPort(uint32 port_index) {
1329 base::AutoLock lock(out_ports_lock_);
1330 auto it = out_ports_.find(port_index);
1331 if (it == out_ports_.end())
1332 return;
1334 int alsa_port = it->second;
1335 snd_seq_delete_simple_port(out_client_, alsa_port);
1336 out_ports_.erase(it);
1339 bool MidiManagerAlsa::Subscribe(uint32 port_index, int client_id, int port_id) {
1340 // Activate port subscription.
1341 snd_seq_port_subscribe_t* subs;
1342 snd_seq_port_subscribe_alloca(&subs);
1343 snd_seq_addr_t sender;
1344 sender.client = client_id;
1345 sender.port = port_id;
1346 snd_seq_port_subscribe_set_sender(subs, &sender);
1347 snd_seq_addr_t dest;
1348 dest.client = in_client_id_;
1349 dest.port = in_port_id_;
1350 snd_seq_port_subscribe_set_dest(subs, &dest);
1351 int err = snd_seq_subscribe_port(in_client_, subs);
1352 if (err != 0) {
1353 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
1354 return false;
1357 // Update our map.
1358 source_map_[AddrToInt(client_id, port_id)] = port_index;
1359 return true;
1362 MidiManager* MidiManager::Create() {
1363 return new MidiManagerAlsa();
1366 } // namespace midi
1367 } // namespace media