Chromium Code Reviews| Index: media/midi/midi_manager_alsa.cc |
| diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc |
| index 9043f1f8cc1223eff2cf268053f25240d979ffb6..ef6a590855eed42bac0d51aa916361eec35094ac 100644 |
| --- a/media/midi/midi_manager_alsa.cc |
| +++ b/media/midi/midi_manager_alsa.cc |
| @@ -15,6 +15,7 @@ |
| #include "base/memory/scoped_vector.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/posix/eintr_wrapper.h" |
| +#include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/thread.h" |
| #include "base/time/time.h" |
| @@ -41,16 +42,15 @@ int AddrToInt(const snd_seq_addr_t* addr) { |
| return (addr->client << 8) | addr->port; |
| } |
| -class CardInfo { |
| - public: |
| - CardInfo(const std::string name, const std::string manufacturer, |
| - const std::string driver) |
| - : name_(name), manufacturer_(manufacturer), driver_(driver) { |
| - } |
| - const std::string name_; |
| - const std::string manufacturer_; |
| - const std::string driver_; |
| -}; |
| +#if defined(USE_UDEV) |
| +// Copied from components/storage_monitor/udev_util_linux.cc. |
| +// TODO(agoode) Move this into a common place. Maybe device/udev_linux? |
|
Takashi Toyoshima
2015/03/06 08:00:12
ditto; TODO(agoode): Move...
Adam Goode
2015/03/06 15:25:02
Done.
|
| +std::string GetUdevDevicePropertyValue(udev_device* udev_device, |
| + const char* key) { |
| + const char* value = device::udev_device_get_property_value(udev_device, key); |
| + return value ? value : std::string(); |
| +} |
| +#endif // defined(USE_UDEV) |
| } // namespace |
| @@ -60,6 +60,9 @@ MidiManagerAlsa::MidiManagerAlsa() |
| out_client_id_(-1), |
| in_port_(-1), |
| decoder_(NULL), |
| +#if defined(USE_UDEV) |
| + udev_(device::udev_new()), |
| +#endif // defined(USE_UDEV) |
| send_thread_("MidiSendThread"), |
| event_thread_("MidiEventThread"), |
| event_thread_shutdown_(false) { |
| @@ -126,11 +129,9 @@ void MidiManagerAlsa::StartInitialization() { |
| return CompleteInitialization(MIDI_INITIALIZATION_ERROR); |
| } |
| - // Use a heuristic to extract the list of manufacturers for the hardware MIDI |
| + // Extract the list of manufacturers for the hardware MIDI |
| // devices. This won't work for all devices. It is also brittle until |
| - // hotplug is implemented. (See http://crbug.com/279097.) |
| - // TODO(agoode): Make manufacturer extraction simple and reliable. |
| - // http://crbug.com/377250. |
| + // hotplug is implemented. (See http://crbug.com/431489.) |
| ScopedVector<CardInfo> cards; |
| snd_ctl_card_info_t* card; |
| snd_rawmidi_info_t* midi_out; |
| @@ -138,8 +139,8 @@ void MidiManagerAlsa::StartInitialization() { |
| snd_ctl_card_info_alloca(&card); |
| snd_rawmidi_info_alloca(&midi_out); |
| snd_rawmidi_info_alloca(&midi_in); |
| - for (int index = -1; !snd_card_next(&index) && index >= 0; ) { |
| - const std::string id = base::StringPrintf("hw:CARD=%i", index); |
| + for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) { |
| + const std::string id = base::StringPrintf("hw:CARD=%i", card_index); |
| snd_ctl_t* handle; |
| int err = snd_ctl_open(&handle, id.c_str(), 0); |
| if (err != 0) { |
| @@ -168,21 +169,14 @@ void MidiManagerAlsa::StartInitialization() { |
| if (!output && !input) |
| continue; |
| + // Compute and save Alsa and udev properties. |
| snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in; |
| - const std::string name = snd_rawmidi_info_get_name(midi); |
| - // We assume that card longname is in the format of |
| - // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect |
| - // a manufacturer name here. |
| - std::string manufacturer; |
| - const std::string card_name = snd_ctl_card_info_get_longname(card); |
| - size_t at_index = card_name.rfind(" at "); |
| - if (std::string::npos != at_index) { |
| - size_t name_index = card_name.rfind(name, at_index - 1); |
| - if (std::string::npos != name_index) |
| - manufacturer = card_name.substr(0, name_index - 1); |
| - } |
| - const std::string driver = snd_ctl_card_info_get_driver(card); |
| - cards.push_back(new CardInfo(name, manufacturer, driver)); |
| + cards.push_back(new CardInfo( |
| + this, |
| + snd_rawmidi_info_get_name(midi), |
| + snd_ctl_card_info_get_longname(card), |
| + snd_ctl_card_info_get_driver(card), |
| + card_index)); |
| } |
| snd_ctl_close(handle); |
| } |
| @@ -214,9 +208,9 @@ void MidiManagerAlsa::StartInitialization() { |
| if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) && |
| (current_card < cards.size())) { |
| const CardInfo* info = cards[current_card]; |
| - if (info->name_ == client_name) { |
| - manufacturer = info->manufacturer_; |
| - driver = info->driver_; |
| + if (info->name() == client_name) { |
| + manufacturer = info->manufacturer(); |
| + driver = info->driver(); |
| current_card++; |
| } |
| } |
| @@ -335,6 +329,61 @@ MidiManagerAlsa::~MidiManagerAlsa() { |
| snd_midi_event_free(*i); |
| } |
| +MidiManagerAlsa::CardInfo::CardInfo( |
| + const MidiManagerAlsa* outer, |
| + const std::string& alsa_name, const std::string& alsa_longname, |
| + const std::string& alsa_driver, int card_index) { |
| + // Get udev properties if available. |
| + std::string udev_id_vendor_enc; |
| + std::string udev_id_vendor_id; |
| + std::string udev_id_vendor_from_database; |
| + |
| +#if defined(USE_UDEV) |
| + const std::string sysname = base::StringPrintf("card%i", card_index); |
| + device::ScopedUdevDevicePtr udev_device( |
| + device::udev_device_new_from_subsystem_sysname( |
| + outer->udev_.get(), "sound", sysname.c_str())); |
| + udev_id_vendor_enc = GetUdevDevicePropertyValue( |
| + udev_device.get(), "ID_VENDOR_ENC"); |
| + udev_id_vendor_id = GetUdevDevicePropertyValue( |
| + udev_device.get(), "ID_VENDOR_ID"); |
| + udev_id_vendor_from_database = GetUdevDevicePropertyValue( |
| + udev_device.get(), "ID_VENDOR_FROM_DATABASE"); |
| + |
| + udev_id_path_ = GetUdevDevicePropertyValue( |
| + udev_device.get(), "ID_PATH"); |
| + udev_id_id_ = GetUdevDevicePropertyValue( |
| + udev_device.get(), "ID_ID"); |
| +#endif // defined(USE_UDEV) |
| + |
| + manufacturer_ = ExtractManufacturerString( |
| + udev_id_vendor_enc, udev_id_vendor_id, udev_id_vendor_from_database, |
| + alsa_name, alsa_longname); |
| +} |
| + |
| +MidiManagerAlsa::CardInfo::~CardInfo() { |
| +} |
| + |
| +const std::string MidiManagerAlsa::CardInfo::name() const { |
| + return alsa_name_; |
| +} |
| + |
| +const std::string MidiManagerAlsa::CardInfo::manufacturer() const { |
| + return manufacturer_; |
| +} |
| + |
| +const std::string MidiManagerAlsa::CardInfo::driver() const { |
| + return driver_; |
| +} |
| + |
| +const std::string MidiManagerAlsa::CardInfo::udev_id_path() const { |
| + return udev_id_path_; |
| +} |
| + |
| +const std::string MidiManagerAlsa::CardInfo::udev_id_id() const { |
| + return udev_id_id_; |
| +} |
| + |
| void MidiManagerAlsa::SendMidiData(uint32 port_index, |
| const std::vector<uint8>& data) { |
| DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| @@ -447,6 +496,62 @@ void MidiManagerAlsa::EventLoop() { |
| base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); |
| } |
| +// static |
| +std::string MidiManagerAlsa::CardInfo::ExtractManufacturerString( |
| + const std::string& udev_id_vendor_enc, |
| + 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) { |
| + // Let's try to determine the manufacturer. Here is the ordered preference |
| + // in extraction: |
| + // 1. Vendor name from the USB device iManufacturer string, stored in |
| + // udev_id_vendor_enc. |
| + // 2. Vendor name from the udev hwid database. |
| + // 3. Heuristic from ALSA. |
| + |
| + // Is the vendor string not just the USB vendor hex id? |
| + std::string udev_id_vendor = UnescapeUdev(udev_id_vendor_enc); |
| + if (udev_id_vendor != udev_id_vendor_id) { |
| + return udev_id_vendor; |
| + } |
| + |
| + // Is there a vendor string in the hardware database? |
| + if (!udev_id_vendor_from_database.empty()) { |
| + return udev_id_vendor_from_database; |
| + } |
| + |
| + // Ok, udev gave us nothing useful. So try a heuristic. |
|
Takashi Toyoshima
2015/03/06 08:00:12
Does this case happen even on USE_UDEV? If so, cur
Adam Goode
2015/03/06 15:25:02
Yes, udev may be available but not have useful inf
|
| + // We assume that card longname is in the format of |
| + // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect |
| + // a manufacturer name here. |
| + size_t at_index = alsa_longname.rfind(" at "); |
| + if (std::string::npos != at_index) { |
| + size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1); |
| + if (std::string::npos != name_index) |
| + return alsa_longname.substr(0, name_index - 1); |
| + } |
| + |
| + // Failure. |
| + return ""; |
| +} |
| + |
| +// static |
| +std::string MidiManagerAlsa::CardInfo::UnescapeUdev(const std::string& s) { |
| + std::string unescaped; |
| + const size_t size = s.size(); |
| + for (size_t i = 0; i < size; ++i) { |
| + char c = s[i]; |
| + if ((i + 3 < size) && c == '\\' && s[i + 1] == 'x') { |
| + c = (HexDigitToInt(s[i + 2]) << 4) + |
| + HexDigitToInt(s[i + 3]); |
| + i += 3; |
| + } |
| + unescaped.push_back(c); |
| + } |
| + return unescaped; |
| +} |
| + |
| MidiManager* MidiManager::Create() { |
| return new MidiManagerAlsa(); |
| } |