vnc_server.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /****************************************************************************
  2. * graphics/vnc/vnc_server.c
  3. *
  4. * Copyright (C) 2016-2017 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include "nuttx/config.h"
  39. #include <stdint.h>
  40. #include <stdlib.h>
  41. #include <semaphore.h>
  42. #include <string.h>
  43. #include <queue.h>
  44. #include <assert.h>
  45. #include <errno.h>
  46. #if defined(CONFIG_VNCSERVER_DEBUG) && !defined(CONFIG_DEBUG_GRAPHICS)
  47. # undef CONFIG_DEBUG_ERROR
  48. # undef CONFIG_DEBUG_WARN
  49. # undef CONFIG_DEBUG_INFO
  50. # undef CONFIG_DEBUG_GRAPHICS_ERROR
  51. # undef CONFIG_DEBUG_GRAPHICS_WARN
  52. # undef CONFIG_DEBUG_GRAPHICS_INFO
  53. # define CONFIG_DEBUG_ERROR 1
  54. # define CONFIG_DEBUG_WARN 1
  55. # define CONFIG_DEBUG_INFO 1
  56. # define CONFIG_DEBUG_GRAPHICS 1
  57. # define CONFIG_DEBUG_GRAPHICS_ERROR 1
  58. # define CONFIG_DEBUG_GRAPHICS_WARN 1
  59. # define CONFIG_DEBUG_GRAPHICS_INFO 1
  60. #endif
  61. #include <debug.h>
  62. #include <arpa/inet.h>
  63. #include <netinet/in.h>
  64. #include <nuttx/kmalloc.h>
  65. #include <nuttx/semaphore.h>
  66. #include <nuttx/net/net.h>
  67. #include "vnc_server.h"
  68. /****************************************************************************
  69. * Public Data
  70. ****************************************************************************/
  71. /* Given a display number as an index, the following array can be used to
  72. * look-up the session structure for that display.
  73. */
  74. FAR struct vnc_session_s *g_vnc_sessions[RFB_MAX_DISPLAYS];
  75. /****************************************************************************
  76. * Private Functions
  77. ****************************************************************************/
  78. /****************************************************************************
  79. * Name: vnc_reset_session
  80. *
  81. * Description:
  82. * Conclude the current VNC session. This function re-initializes the
  83. * session structure; it does not free either the session structure nor
  84. * the framebuffer so that they may be re-used.
  85. *
  86. * Input Parameters:
  87. * session - An instance of the session structure.
  88. *
  89. * Returned Value:
  90. * None
  91. *
  92. ****************************************************************************/
  93. static void vnc_reset_session(FAR struct vnc_session_s *session,
  94. FAR uint8_t *fb, int display)
  95. {
  96. int i;
  97. /* Close any open sockets */
  98. if (session->state >= VNCSERVER_CONNECTED)
  99. {
  100. psock_close(&session->connect);
  101. psock_close(&session->listen);
  102. }
  103. /* [Re-]initialize the session. */
  104. memset(&session->connect, 0, sizeof(struct socket));
  105. session->connect.s_crefs = 1;
  106. memset(&session->listen, 0, sizeof(struct socket));
  107. session->listen.s_crefs = 1;
  108. /* Put all of the pre-allocated update structures into the freelist */
  109. sq_init(&session->updqueue);
  110. sq_init(&session->updfree);
  111. for (i = 0; i < CONFIG_VNCSERVER_NUPDATES; i++)
  112. {
  113. sq_addlast((FAR sq_entry_t *)&session->updpool[i], &session->updfree);
  114. }
  115. /* Set the INITIALIZED state */
  116. nxsem_reset(&session->freesem, CONFIG_VNCSERVER_NUPDATES);
  117. nxsem_reset(&session->queuesem, 0);
  118. session->fb = fb;
  119. session->display = display;
  120. session->state = VNCSERVER_INITIALIZED;
  121. session->nwhupd = 0;
  122. session->change = true;
  123. /* Careful not to disturb the keyboard/mouse callouts set by
  124. * vnc_fbinitialize(). Client related data left in garbage state.
  125. */
  126. }
  127. /****************************************************************************
  128. * Name: vnc_connect
  129. *
  130. * Description:
  131. * Wait for a connection from the VNC client
  132. *
  133. * Input Parameters:
  134. * session - An instance of the session structure.
  135. * port - The listen port to use
  136. *
  137. * Returned Value:
  138. * Returns zero (OK) on success; a negated errno value on failure.
  139. *
  140. ****************************************************************************/
  141. static int vnc_connect(FAR struct vnc_session_s *session, int port)
  142. {
  143. struct sockaddr_in addr;
  144. int ret;
  145. ginfo("Connecting display %d\n", session->display);
  146. /* Create a listening socket */
  147. addr.sin_family = AF_INET;
  148. addr.sin_port = htons(port);
  149. addr.sin_addr.s_addr = INADDR_ANY;
  150. ret = psock_socket(AF_INET, SOCK_STREAM, 0, &session->listen);
  151. if (ret < 0)
  152. {
  153. return ret;
  154. }
  155. /* Bind the listening socket to a local address */
  156. ret = psock_bind(&session->listen, (struct sockaddr *)&addr,
  157. sizeof(struct sockaddr_in));
  158. if (ret < 0)
  159. {
  160. goto errout_with_listener;
  161. }
  162. /* Listen for a connection */
  163. ret = psock_listen(&session->listen, 5);
  164. if (ret < 0)
  165. {
  166. goto errout_with_listener;
  167. }
  168. /* Connect to the client */
  169. ginfo("Accepting connection for Display %d\n", session->display);
  170. ret = psock_accept(&session->listen, NULL, NULL, &session->connect);
  171. if (ret < 0)
  172. {
  173. goto errout_with_listener;
  174. }
  175. ginfo("Display %d connected\n", session->display);
  176. session->state = VNCSERVER_CONNECTED;
  177. return OK;
  178. errout_with_listener:
  179. psock_close(&session->listen);
  180. return ret;
  181. }
  182. /****************************************************************************
  183. * Pubic Functions
  184. ****************************************************************************/
  185. /****************************************************************************
  186. * Name: vnc_server
  187. *
  188. * Description:
  189. * The VNC server daemon. This daemon is implemented as a kernel thread.
  190. *
  191. * Input Parameters:
  192. * Standard kernel thread arguments (all ignored)
  193. *
  194. * Returned Value:
  195. * This function does not return.
  196. *
  197. ****************************************************************************/
  198. int vnc_server(int argc, FAR char *argv[])
  199. {
  200. FAR struct vnc_session_s *session;
  201. FAR uint8_t *fb;
  202. int display;
  203. int ret;
  204. /* A single argument is expected: A diplay port number in ASCII form */
  205. if (argc != 2)
  206. {
  207. /* In this case the start-up logic will probably hang, waiting for the
  208. * display-related semaphore to be set.
  209. */
  210. gerr("ERROR: Unexpected number of arguments: %d\n", argc);
  211. ret = -EINVAL;
  212. goto errout_with_hang;
  213. }
  214. display = atoi(argv[1]);
  215. if (display < 0 || display >= RFB_MAX_DISPLAYS)
  216. {
  217. /* In this case the start-up logic will probably hang, waiting for the
  218. * display-related semaphore to be set.
  219. */
  220. gerr("ERROR: Invalid display number: %d\n", display);
  221. ret = -EINVAL;
  222. goto errout_with_hang;
  223. }
  224. ginfo("Server started for Display %d\n", display);
  225. /* Allocate the framebuffer memory. We rely on the fact that
  226. * the KMM allocator will align memory to 32-bits or better.
  227. */
  228. fb = (FAR uint8_t *)kmm_zalloc(RFB_SIZE);
  229. if (fb == NULL)
  230. {
  231. gerr("ERROR: Failed to allocate framebuffer memory: %lu KB\n",
  232. (unsigned long)(RFB_SIZE / 1024));
  233. ret = -ENOMEM;
  234. goto errout_with_post;
  235. }
  236. /* Allocate a session structure for this display */
  237. session = kmm_zalloc(sizeof(struct vnc_session_s));
  238. if (session == NULL)
  239. {
  240. gerr("ERROR: Failed to allocate session\n");
  241. ret = -ENOMEM;
  242. goto errout_with_fb;
  243. }
  244. g_vnc_sessions[display] = session;
  245. nxsem_init(&session->freesem, 0, CONFIG_VNCSERVER_NUPDATES);
  246. nxsem_init(&session->queuesem, 0, 0);
  247. /* Inform any waiter that we have started */
  248. vnc_reset_session(session, fb, display);
  249. nxsem_post(&g_fbstartup[display].fbinit);
  250. /* Loop... handling each each VNC client connection to this display. Only
  251. * a single client is allowed for each display.
  252. */
  253. for (; ; )
  254. {
  255. /* Release the last session and [Re-]initialize the session structure
  256. * for the next connection.
  257. */
  258. vnc_reset_session(session, fb, display);
  259. g_fbstartup[display].result = -EBUSY;
  260. nxsem_reset(&g_fbstartup[display].fbconnect, 0);
  261. /* Establish a connection with the VNC client */
  262. ret = vnc_connect(session, RFB_DISPLAY_PORT(display));
  263. if (ret >= 0)
  264. {
  265. ginfo("New VNC connection\n");
  266. /* Perform the VNC initialization sequence after the client has
  267. * successfully connected to the server. Negotiate security,
  268. * framebuffer and color properties.
  269. */
  270. ret = vnc_negotiate(session);
  271. if (ret < 0)
  272. {
  273. gerr("ERROR: Failed to negotiate security/framebuffer: %d\n",
  274. ret);
  275. continue;
  276. }
  277. /* Start the VNC updater thread that sends all Server-to-Client
  278. * messages.
  279. */
  280. ret = vnc_start_updater(session);
  281. if (ret < 0)
  282. {
  283. gerr("ERROR: Failed to start updater thread: %d\n", ret);
  284. continue;
  285. }
  286. /* Let the framebuffer driver know that we are ready to preform
  287. * updates.
  288. */
  289. g_fbstartup[display].result = OK;
  290. nxsem_post(&g_fbstartup[display].fbconnect);
  291. /* Run the VNC receiver on this trhead. The VNC receiver handles
  292. * all Client-to-Server messages. The VNC receiver function does
  293. * not return until the session has been terminated (or an error
  294. * occurs).
  295. */
  296. ret = vnc_receiver(session);
  297. ginfo("Session terminated with %d\n", ret);
  298. UNUSED(ret);
  299. /* Stop the VNC updater thread. */
  300. ret = vnc_stop_updater(session);
  301. if (ret < 0)
  302. {
  303. gerr("ERROR: Failed to stop updater thread: %d\n", ret);
  304. }
  305. }
  306. }
  307. errout_with_fb:
  308. kmm_free(fb);
  309. errout_with_post:
  310. g_fbstartup[display].result = ret;
  311. nxsem_post(&g_fbstartup[display].fbconnect);
  312. errout_with_hang:
  313. return EXIT_FAILURE;
  314. }