blob: 72fd187b0bf552f90146a28d180e908d1bf06762 [file] [log] [blame]
Prashant Malanifd1e2002017-08-09 13:22:59 -07001// Copyright 2017 The Chromium OS 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#ifndef MIDIS_SEQ_HANDLER_H_
6#define MIDIS_SEQ_HANDLER_H_
7
8#include <memory>
9
Hidehiko Abeb9eb3692019-09-19 01:15:11 +090010#include <base/files/file_descriptor_watcher_posix.h>
Prashant Malanifd1e2002017-08-09 13:22:59 -070011#include <base/memory/weak_ptr.h>
Prashant Malani3c540362017-09-28 13:35:14 -070012#include <gtest/gtest_prod.h>
Prashant Malanifd1e2002017-08-09 13:22:59 -070013
14#include "midis/device.h"
15#include "midis/device_tracker.h"
Prashant Malanifd1e2002017-08-09 13:22:59 -070016
17namespace midis {
18
19class DeviceTracker;
20
21// Class to handle all interactions with the ALSA sequencer interface.
22// NOTE: The term "input" refers to data received *from* the MIDI H/W and
23// external clients that are registered to the ALSA sequencer interfance. The
24// term "output" refers to data that a client of midis write *to* MIDI H/W and
25// external clients.
Prashant Malanie8f0c672018-03-16 17:03:58 -070026class SeqHandler {
Prashant Malanifd1e2002017-08-09 13:22:59 -070027 public:
28 using AddDeviceCallback = base::Callback<void(std::unique_ptr<Device>)>;
29 using RemoveDeviceCallback = base::Callback<void(uint32_t, uint32_t)>;
30 using HandleReceiveDataCallback =
31 base::Callback<void(uint32_t, uint32_t, uint32_t, const char*, size_t)>;
32 using IsDevicePresentCallback = base::Callback<bool(uint32_t, uint32_t)>;
33 using IsPortPresentCallback =
34 base::Callback<bool(uint32_t, uint32_t, uint32_t)>;
35
36 struct SeqDeleter {
37 void operator()(snd_seq_t* seq) const { snd_seq_close(seq); }
38 };
39
40 struct MidiEventDeleter {
41 void operator()(snd_midi_event_t* coder) const {
42 snd_midi_event_free(coder);
43 }
44 };
45
46 using ScopedMidiEventPtr =
47 std::unique_ptr<snd_midi_event_t, MidiEventDeleter>;
48 using ScopedSeqPtr = std::unique_ptr<snd_seq_t, SeqDeleter>;
49
50 SeqHandler(AddDeviceCallback add_device_cb,
51 RemoveDeviceCallback remove_device_cb,
52 HandleReceiveDataCallback handle_rx_data_cb,
53 IsDevicePresentCallback is_device_present_cb,
54 IsPortPresentCallback is_port_present_cb);
55
Prashant Malanie8f0c672018-03-16 17:03:58 -070056 virtual ~SeqHandler() = default;
Prashant Malanifd1e2002017-08-09 13:22:59 -070057
58 // Initializes the ALSA seq interface. Creates client handles for input and
59 // output, as well create an input port to receive messages (announce as
60 // well as MIDI data) from ALSA seq. Also starts off the file watcher which
61 // watches for events on the input port.
Prashant Malanie8f0c672018-03-16 17:03:58 -070062 bool InitSeq();
63 void ProcessAlsaClientFd();
Prashant Malanifd1e2002017-08-09 13:22:59 -070064
65 // Creates a Device object and runs the necessary callback to register that
66 // object with DeviceTracker, stored in |add_device_cb_|
Prashant Malanie8f0c672018-03-16 17:03:58 -070067 virtual void AddSeqDevice(uint32_t device_id);
Prashant Malanifd1e2002017-08-09 13:22:59 -070068
69 // At present, we don't support hotplugging of individual ports in devices.
70 // so, we enumerate all the available ports in AddAlsaDevice().
71 // This function is here merely to handle MIDI events associated with any
72 // port being added or removed later (and to print an error message, since
73 // we don't support it yet)
Prashant Malanie8f0c672018-03-16 17:03:58 -070074 virtual void AddSeqPort(uint32_t device_id, uint32_t port_id);
Prashant Malanifd1e2002017-08-09 13:22:59 -070075
76 // Runs the relevant callback, stored in |remove_device_cb_|, when a MIDI H/W
77 // device or external client is removed from the ALSA sequencer interface.
Prashant Malanie8f0c672018-03-16 17:03:58 -070078 virtual void RemoveSeqDevice(uint32_t device_id);
Prashant Malanifd1e2002017-08-09 13:22:59 -070079
Prashant Malanie8f0c672018-03-16 17:03:58 -070080 virtual void RemoveSeqPort(uint32_t device_id, uint32_t port_id);
Prashant Malanifd1e2002017-08-09 13:22:59 -070081
82 // Callback to run when starting an input port (establishes a subscription,
83 // and creates a relevant port on the server side, in necessary).
84 // Returns true on success, false otherwise.
Prashant Malanie8f0c672018-03-16 17:03:58 -070085 bool SubscribeInPort(uint32_t device_id, uint32_t port_id);
Prashant Malanifd1e2002017-08-09 13:22:59 -070086
87 // Callback to run when starting an input port (establishes a subscription,
88 // and creates a relevant port on the server side, in necessary).
89 // Returns created seq port id success, -1 otherwise.
Prashant Malanie8f0c672018-03-16 17:03:58 -070090 int SubscribeOutPort(uint32_t device_id, uint32_t port_id);
Prashant Malanifd1e2002017-08-09 13:22:59 -070091
92 // The following two functions undo the work of the Subscribe*Port() function,
93 // for input and output ports respectively.
Prashant Malanie8f0c672018-03-16 17:03:58 -070094 void UnsubscribeInPort(uint32_t device_id, uint32_t port_id);
95 void UnsubscribeOutPort(int out_port_id);
Prashant Malanifd1e2002017-08-09 13:22:59 -070096
Prashant Malani3c540362017-09-28 13:35:14 -070097 // Encodes the bytes in a MIDI buffer into the provided |encoder|.
98 bool EncodeMidiBytes(int out_port_id,
99 snd_seq_t* out_client,
100 const uint8_t* buffer,
101 size_t buffer_len,
102 snd_midi_event_t* encoder);
103
Prashant Malanifd1e2002017-08-09 13:22:59 -0700104 // Callback to send MIDI data to the H/W. This callback is generally called by
105 // a Device handler which receives MIDI data from a client (e.g ARC++). The
106 // Device handler will in turn be called by a Client handler which is
107 // listening for data from it's client.
Tom Hughesaebcdce2020-08-27 15:18:39 -0700108 void SendMidiData(int out_port_id, const uint8_t* buffer, size_t buf_len);
Prashant Malanifd1e2002017-08-09 13:22:59 -0700109
110 // This function processes the MIDI data received from H/W or an external
111 // client, and invokes the callback |handle_rx_data_cb_| which handles the
112 // data accordingly.
Ben Chanba3382c2019-09-19 14:08:30 -0700113 virtual void ProcessMidiEvent(snd_seq_event_t* event);
Prashant Malanifd1e2002017-08-09 13:22:59 -0700114
Prashant Malani3c540362017-09-28 13:35:14 -0700115 // Wrappers for functions that interact with the ALSA Sequencer interface.
116 // These are kept separately, because the intention is to mock these functions
117 // in unit tests.
118 virtual int SndSeqEventOutputDirect(snd_seq_t* out_client,
119 snd_seq_event_t* event);
Prashant Malania9bbf542017-10-19 18:27:18 -0700120 virtual int SndSeqEventInput(snd_seq_t* in_client, snd_seq_event_t** ev);
121 virtual int SndSeqEventInputPending(snd_seq_t* in_client,
122 int fetch_sequencer);
Prashant Malani3c540362017-09-28 13:35:14 -0700123
124 protected:
125 // For testing purposes.
126 SeqHandler();
127
Prashant Malanifd1e2002017-08-09 13:22:59 -0700128 private:
Prashant Malani3c540362017-09-28 13:35:14 -0700129 friend class SeqHandlerTest;
Prashant Malani519dd742018-03-15 22:01:50 -0700130 friend class SeqHandlerFuzzer;
Prashant Malani3c540362017-09-28 13:35:14 -0700131 FRIEND_TEST(SeqHandlerTest, TestEncodeBytes);
Prashant Malania9bbf542017-10-19 18:27:18 -0700132 FRIEND_TEST(SeqHandlerTest, TestProcessAlsaClientFdPositive);
Prashant Malani78b59c32017-10-24 22:54:51 -0700133 FRIEND_TEST(SeqHandlerTest, TestProcessMidiEventsPositive);
134 FRIEND_TEST(SeqHandlerTest, TestProcessMidiEventsNegative);
Prashant Malani3c540362017-09-28 13:35:14 -0700135
Prashant Malani8ca4ab32017-10-03 15:12:49 -0700136 // Enumerates all clients which are already connected to the ALSA Sequencer.
137 void EnumerateExistingDevices();
138
Prashant Malani78b59c32017-10-24 22:54:51 -0700139 // Creates a MIDI event wrapped in a ScopedMidiEventPtr.
140 static ScopedMidiEventPtr CreateMidiEvent(size_t buf_size);
141
Prashant Malanifd1e2002017-08-09 13:22:59 -0700142 std::unique_ptr<snd_seq_t, SeqDeleter> in_client_;
143 std::unique_ptr<snd_seq_t, SeqDeleter> out_client_;
144 std::unique_ptr<snd_midi_event_t, MidiEventDeleter> decoder_;
145 int in_client_id_;
146 int out_client_id_;
147 int in_port_id_;
148 std::unique_ptr<pollfd> pfd_;
Hidehiko Abeb9eb3692019-09-19 01:15:11 +0900149 std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_;
Prashant Malanifd1e2002017-08-09 13:22:59 -0700150
151 AddDeviceCallback add_device_cb_;
152 RemoveDeviceCallback remove_device_cb_;
153 HandleReceiveDataCallback handle_rx_data_cb_;
154 IsDevicePresentCallback is_device_present_cb_;
155 IsPortPresentCallback is_port_present_cb_;
156 base::WeakPtrFactory<SeqHandler> weak_factory_;
157
158 DISALLOW_COPY_AND_ASSIGN(SeqHandler);
159};
160
161} // namespace midis
162
163#endif // MIDIS_SEQ_HANDLER_H_