controls.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * (c) Philippe G. 2019, philippe_44@outlook.com
  3. *
  4. * This software is released under the MIT License.
  5. * https://opensource.org/licenses/MIT
  6. *
  7. */
  8. #include "squeezelite.h"
  9. #include "platform_config.h"
  10. #include "audio_controls.h"
  11. static log_level loglevel = lINFO;
  12. #define DOWN_OFS 0x10000
  13. #define UP_OFS 0x20000
  14. // numbers are simply 0..9 but are not used
  15. // arrow_right.down = 0001000e seems to be missing ...
  16. enum { BUTN_POWER_FRONT = 0X0A, BUTN_ARROW_UP, BUTN_ARROW_DOWN, BUTN_ARROW_LEFT, BUTN_KNOB_PUSH, BUTN_SEARCH,
  17. BUTN_REW, BUTN_FWD, BUTN_PLAY, BUTN_ADD, BUTN_BRIGHTNESS, BUTN_NOW_PLAYING,
  18. BUTN_PAUSE = 0X17, BUTN_BROWSE, BUTN_VOLUP_FRONT, BUTN_VOLDOWN_FRONT, BUTN_SIZE, BUTN_VISUAL, BUTN_VOLUMEMODE,
  19. BUTN_PRESET_1 = 0X23, BUTN_PRESET_2, BUTN_PRESET_3, BUTN_PRESET_4, BUTN_PRESET_5, BUTN_PRESET_6, BUTN_SNOOZE,
  20. BUTN_KNOB_LEFT = 0X5A, BUTN_KNOB_RIGHT };
  21. #define BUTN_ARROW_RIGHT BUTN_KNOB_PUSH
  22. #pragma pack(push, 1)
  23. struct BUTN_header {
  24. char opcode[4];
  25. u32_t length;
  26. u32_t jiffies;
  27. u32_t button;
  28. };
  29. struct IR_header {
  30. char opcode[4];
  31. u32_t length;
  32. u32_t jiffies;
  33. u8_t format; // unused
  34. u8_t bits; // unused
  35. u32_t code;
  36. };
  37. #pragma pack(pop)
  38. static in_addr_t server_ip;
  39. static u16_t server_hport;
  40. static u16_t server_cport;
  41. static int cli_sock = -1;
  42. static u8_t mac[6];
  43. static void (*chained_notify)(in_addr_t, u16_t, u16_t);
  44. static bool raw_mode;
  45. static void cli_send_cmd(char *cmd);
  46. /****************************************************************************************
  47. * Send BUTN
  48. */
  49. static void sendBUTN(int code, bool pressed) {
  50. struct BUTN_header pkt_header;
  51. memset(&pkt_header, 0, sizeof(pkt_header));
  52. memcpy(&pkt_header.opcode, "BUTN", 4);
  53. pkt_header.length = htonl(sizeof(pkt_header) - 8);
  54. pkt_header.jiffies = htonl(gettime_ms());
  55. pkt_header.button = htonl(code + (pressed ? DOWN_OFS : UP_OFS));
  56. LOG_INFO("sending BUTN code %04x %s", code, pressed ? "down" : "up");
  57. LOCK_P;
  58. send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
  59. UNLOCK_P;
  60. }
  61. /****************************************************************************************
  62. * Send IR
  63. */
  64. static void sendIR(u16_t addr, u16_t cmd) {
  65. struct IR_header pkt_header;
  66. memset(&pkt_header, 0, sizeof(pkt_header));
  67. memcpy(&pkt_header.opcode, "IR ", 4);
  68. pkt_header.length = htonl(sizeof(pkt_header) - 8);
  69. pkt_header.jiffies = htonl(gettime_ms());
  70. pkt_header.format = pkt_header.bits = 0;
  71. pkt_header.code = htonl((addr << 16) | cmd);
  72. LOG_INFO("sending IR code %04x", (addr << 16) | cmd);
  73. LOCK_P;
  74. send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
  75. UNLOCK_P;
  76. }
  77. static void lms_toggle(bool pressed) {
  78. if (raw_mode) {
  79. sendBUTN(BUTN_PAUSE, pressed);
  80. } else {
  81. cli_send_cmd("pause");
  82. }
  83. }
  84. static void lms_pause(bool pressed) {
  85. if (raw_mode) {
  86. sendBUTN(BUTN_PAUSE, pressed);
  87. } else {
  88. cli_send_cmd("pause 1");
  89. }
  90. }
  91. static void lms_stop(bool pressed) {
  92. cli_send_cmd("button stop");
  93. }
  94. #define LMS_CALLBACK(N,B,E) \
  95. static void lms_##N (bool pressed) { \
  96. if (raw_mode) { \
  97. sendBUTN( BUTN_##B , pressed ); \
  98. } else { \
  99. cli_send_cmd("button" " " #E); \
  100. } \
  101. }
  102. LMS_CALLBACK(power, POWER_FRONT, power)
  103. LMS_CALLBACK(play, PLAY, play.single)
  104. LMS_CALLBACK(volup, VOLUP_FRONT, volup)
  105. LMS_CALLBACK(voldown, VOLDOWN_FRONT, voldown)
  106. LMS_CALLBACK(rew, REW, rew.repeat)
  107. LMS_CALLBACK(fwd, FWD, fwd.repeat)
  108. LMS_CALLBACK(prev, REW, rew)
  109. LMS_CALLBACK(next, FWD, fwd)
  110. LMS_CALLBACK(up, ARROW_UP, arrow_up)
  111. LMS_CALLBACK(down, ARROW_DOWN, arrow_down)
  112. LMS_CALLBACK(left, ARROW_LEFT, arrow_left)
  113. LMS_CALLBACK(right, ARROW_RIGHT, arrow_right)
  114. LMS_CALLBACK(pre1, PRESET_1, preset_1.single)
  115. LMS_CALLBACK(pre2, PRESET_2, preset_2.single)
  116. LMS_CALLBACK(pre3, PRESET_3, preset_3.single)
  117. LMS_CALLBACK(pre4, PRESET_4, preset_4.single)
  118. LMS_CALLBACK(pre5, PRESET_5, preset_5.single)
  119. LMS_CALLBACK(pre6, PRESET_6, preset_6.single)
  120. LMS_CALLBACK(knob_left, KNOB_LEFT, knob_left)
  121. LMS_CALLBACK(knob_right, KNOB_RIGHT, knob_right)
  122. LMS_CALLBACK(knob_push, KNOB_PUSH, knob_push)
  123. const actrls_t LMS_controls = {
  124. lms_power,
  125. lms_volup, lms_voldown, // volume up, volume down
  126. lms_toggle, lms_play, // toggle, play
  127. lms_pause, lms_stop, // pause, stop
  128. lms_rew, lms_fwd, // rew, fwd
  129. lms_prev, lms_next, // prev, next
  130. lms_up, lms_down,
  131. lms_left, lms_right,
  132. lms_pre1, lms_pre2, lms_pre3, lms_pre4, lms_pre5, lms_pre6,
  133. lms_knob_left, lms_knob_right, lms_knob_push,
  134. };
  135. /****************************************************************************************
  136. *
  137. */
  138. static void connect_cli_socket(void) {
  139. struct sockaddr_in addr = {
  140. .sin_family = AF_INET,
  141. .sin_addr.s_addr = server_ip,
  142. .sin_port = htons(server_cport),
  143. };
  144. socklen_t addrlen = sizeof(addr);
  145. cli_sock = socket(AF_INET, SOCK_STREAM, 0);
  146. if (connect(cli_sock, (struct sockaddr *) &addr, addrlen) < 0) {
  147. LOG_ERROR("unable to connect to server %s:%hu with cli", inet_ntoa(server_ip), server_cport);
  148. closesocket(cli_sock);
  149. cli_sock = -1;
  150. }
  151. }
  152. /****************************************************************************************
  153. *
  154. */
  155. static void cli_send_cmd(char *cmd) {
  156. char packet[96];
  157. int len;
  158. len = sprintf(packet, "%02x:%02x:%02x:%02x:%02x:%02x %s\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], cmd);
  159. LOG_DEBUG("sending command %s at %s:%hu", packet, inet_ntoa(server_ip), server_cport);
  160. if (cli_sock < 0) connect_cli_socket();
  161. if (send(cli_sock, packet, len, MSG_DONTWAIT) < 0) {
  162. closesocket(cli_sock);
  163. cli_sock = -1;
  164. LOG_WARN("cannot send CLI %s", packet);
  165. }
  166. // need to empty the RX buffer otherwise we'll lock the TCP/IP stack
  167. len = recv(cli_sock, packet, 96, MSG_DONTWAIT);
  168. }
  169. /****************************************************************************************
  170. * Notification when server changes
  171. */
  172. static void notify(in_addr_t ip, u16_t hport, u16_t cport) {
  173. server_ip = ip;
  174. server_hport = hport;
  175. server_cport = cport;
  176. // close existing CLI connection and open new one
  177. if (cli_sock >= 0) closesocket(cli_sock);
  178. connect_cli_socket();
  179. LOG_INFO("notified server %s hport %hu cport %hu", inet_ntoa(ip), hport, cport);
  180. if (chained_notify) (*chained_notify)(ip, hport, cport);
  181. }
  182. /****************************************************************************************
  183. * IR handler
  184. */
  185. static bool ir_handler(u16_t addr, u16_t cmd) {
  186. sendIR(addr, cmd);
  187. return true;
  188. }
  189. /****************************************************************************************
  190. * Initialize controls - shall be called once from output_init_embedded
  191. */
  192. void sb_controls_init(void) {
  193. char *p = config_alloc_get_default(NVS_TYPE_STR, "lms_ctrls_raw", "n", 0);
  194. raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y');
  195. free(p);
  196. LOG_INFO("initializing audio (buttons/rotary/ir) controls (raw:%u)", raw_mode);
  197. get_mac(mac);
  198. actrls_set_default(LMS_controls, raw_mode, NULL, ir_handler);
  199. chained_notify = server_notify;
  200. server_notify = notify;
  201. }