blob: 20c71dd15b513f4fa71e5298347e2b8889dab07b [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
10#include <base/memory/weak_ptr.h>
11#include <brillo/message_loops/message_loop.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"
16#include "midis/seq_handler_interface.h"
17
18namespace midis {
19
20class DeviceTracker;
21
22// Class to handle all interactions with the ALSA sequencer interface.
23// NOTE: The term "input" refers to data received *from* the MIDI H/W and
24// external clients that are registered to the ALSA sequencer interfance. The
25// term "output" refers to data that a client of midis write *to* MIDI H/W and
26// external clients.
27class SeqHandler : public SeqHandlerInterface {
28 public:
29 using AddDeviceCallback = base::Callback<void(std::unique_ptr<Device>)>;
30 using RemoveDeviceCallback = base::Callback<void(uint32_t, uint32_t)>;
31 using HandleReceiveDataCallback =
32 base::Callback<void(uint32_t, uint32_t, uint32_t, const char*, size_t)>;
33 using IsDevicePresentCallback = base::Callback<bool(uint32_t, uint32_t)>;
34 using IsPortPresentCallback =
35 base::Callback<bool(uint32_t, uint32_t, uint32_t)>;
36
37 struct SeqDeleter {
38 void operator()(snd_seq_t* seq) const { snd_seq_close(seq); }
39 };
40
41 struct MidiEventDeleter {
42 void operator()(snd_midi_event_t* coder) const {
43 snd_midi_event_free(coder);
44 }
45 };
46
47 using ScopedMidiEventPtr =
48 std::unique_ptr<snd_midi_event_t, MidiEventDeleter>;
49 using ScopedSeqPtr = std::unique_ptr<snd_seq_t, SeqDeleter>;
50
51 SeqHandler(AddDeviceCallback add_device_cb,
52 RemoveDeviceCallback remove_device_cb,
53 HandleReceiveDataCallback handle_rx_data_cb,
54 IsDevicePresentCallback is_device_present_cb,
55 IsPortPresentCallback is_port_present_cb);
56
57 ~SeqHandler() override = default;
58
59 // Initializes the ALSA seq interface. Creates client handles for input and
60 // output, as well create an input port to receive messages (announce as
61 // well as MIDI data) from ALSA seq. Also starts off the file watcher which
62 // watches for events on the input port.
63 bool InitSeq() override;
64 void ProcessAlsaClientFd() override;
65
66 // Creates a Device object and runs the necessary callback to register that
67 // object with DeviceTracker, stored in |add_device_cb_|
68 void AddSeqDevice(uint32_t device_id) override;
69
70 // At present, we don't support hotplugging of individual ports in devices.
71 // so, we enumerate all the available ports in AddAlsaDevice().
72 // This function is here merely to handle MIDI events associated with any
73 // port being added or removed later (and to print an error message, since
74 // we don't support it yet)
75 void AddSeqPort(uint32_t device_id, uint32_t port_id) override;
76
77 // Runs the relevant callback, stored in |remove_device_cb_|, when a MIDI H/W
78 // device or external client is removed from the ALSA sequencer interface.
79 void RemoveSeqDevice(uint32_t device_id) override;
80
81 void RemoveSeqPort(uint32_t device_id, uint32_t port_id) override;
82
83 // Callback to run when starting an input port (establishes a subscription,
84 // and creates a relevant port on the server side, in necessary).
85 // Returns true on success, false otherwise.
86 bool SubscribeInPort(uint32_t device_id, uint32_t port_id) override;
87
88 // Callback to run when starting an input port (establishes a subscription,
89 // and creates a relevant port on the server side, in necessary).
90 // Returns created seq port id success, -1 otherwise.
91 int SubscribeOutPort(uint32_t device_id, uint32_t port_id) override;
92
93 // The following two functions undo the work of the Subscribe*Port() function,
94 // for input and output ports respectively.
95 void UnsubscribeInPort(uint32_t device_id, uint32_t port_id) override;
96 void UnsubscribeOutPort(int out_port_id) override;
97
Prashant Malani3c540362017-09-28 13:35:14 -070098 // Encodes the bytes in a MIDI buffer into the provided |encoder|.
99 bool EncodeMidiBytes(int out_port_id,
100 snd_seq_t* out_client,
101 const uint8_t* buffer,
102 size_t buffer_len,
103 snd_midi_event_t* encoder);
104
Prashant Malanifd1e2002017-08-09 13:22:59 -0700105 // Callback to send MIDI data to the H/W. This callback is generally called by
106 // a Device handler which receives MIDI data from a client (e.g ARC++). The
107 // Device handler will in turn be called by a Client handler which is
108 // listening for data from it's client.
109 void SendMidiData(int out_port_id,
110 const uint8_t* buffer,
111 size_t buf_len) override;
112
113 // This function processes the MIDI data received from H/W or an external
114 // client, and invokes the callback |handle_rx_data_cb_| which handles the
115 // data accordingly.
116 void ProcessMidiEvent(snd_seq_event_t* event) override;
117
Prashant Malani3c540362017-09-28 13:35:14 -0700118 // Wrappers for functions that interact with the ALSA Sequencer interface.
119 // These are kept separately, because the intention is to mock these functions
120 // in unit tests.
121 virtual int SndSeqEventOutputDirect(snd_seq_t* out_client,
122 snd_seq_event_t* event);
Prashant Malania9bbf542017-10-19 18:27:18 -0700123 virtual int SndSeqEventInput(snd_seq_t* in_client, snd_seq_event_t** ev);
124 virtual int SndSeqEventInputPending(snd_seq_t* in_client,
125 int fetch_sequencer);
Prashant Malani3c540362017-09-28 13:35:14 -0700126
127 protected:
128 // For testing purposes.
129 SeqHandler();
130
Prashant Malanifd1e2002017-08-09 13:22:59 -0700131 private:
Prashant Malani3c540362017-09-28 13:35:14 -0700132 friend class SeqHandlerTest;
133 FRIEND_TEST(SeqHandlerTest, TestEncodeBytes);
Prashant Malania9bbf542017-10-19 18:27:18 -0700134 FRIEND_TEST(SeqHandlerTest, TestProcessAlsaClientFdPositive);
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 Malanifd1e2002017-08-09 13:22:59 -0700139 std::unique_ptr<snd_seq_t, SeqDeleter> in_client_;
140 std::unique_ptr<snd_seq_t, SeqDeleter> out_client_;
141 std::unique_ptr<snd_midi_event_t, MidiEventDeleter> decoder_;
142 int in_client_id_;
143 int out_client_id_;
144 int in_port_id_;
145 std::unique_ptr<pollfd> pfd_;
146 brillo::MessageLoop::TaskId taskid_;
147
148 AddDeviceCallback add_device_cb_;
149 RemoveDeviceCallback remove_device_cb_;
150 HandleReceiveDataCallback handle_rx_data_cb_;
151 IsDevicePresentCallback is_device_present_cb_;
152 IsPortPresentCallback is_port_present_cb_;
153 base::WeakPtrFactory<SeqHandler> weak_factory_;
154
155 DISALLOW_COPY_AND_ASSIGN(SeqHandler);
156};
157
158} // namespace midis
159
160#endif // MIDIS_SEQ_HANDLER_H_