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