controls.c 7.1 KB

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