wifi_display.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /*
  2. * wpa_supplicant - Wi-Fi Display
  3. * Copyright (c) 2011, Atheros Communications, Inc.
  4. * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
  5. *
  6. * This software may be distributed under the terms of the BSD license.
  7. * See README for more details.
  8. */
  9. #include "includes.h"
  10. #include "common.h"
  11. #include "p2p/p2p.h"
  12. #include "common/ieee802_11_defs.h"
  13. #include "wpa_supplicant_i.h"
  14. #include "wifi_display.h"
  15. #define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
  16. int wifi_display_init(struct wpa_global *global)
  17. {
  18. global->wifi_display = 1;
  19. return 0;
  20. }
  21. void wifi_display_deinit(struct wpa_global *global)
  22. {
  23. int i;
  24. for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
  25. wpabuf_free(global->wfd_subelem[i]);
  26. global->wfd_subelem[i] = NULL;
  27. }
  28. }
  29. struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global)
  30. {
  31. struct wpabuf *ie;
  32. size_t len;
  33. int i;
  34. if (global->p2p == NULL)
  35. return NULL;
  36. len = 0;
  37. for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
  38. if (global->wfd_subelem[i])
  39. len += wpabuf_len(global->wfd_subelem[i]);
  40. }
  41. ie = wpabuf_alloc(len);
  42. if (ie == NULL)
  43. return NULL;
  44. for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
  45. if (global->wfd_subelem[i])
  46. wpabuf_put_buf(ie, global->wfd_subelem[i]);
  47. }
  48. return ie;
  49. }
  50. static int wifi_display_update_wfd_ie(struct wpa_global *global)
  51. {
  52. struct wpabuf *ie, *buf;
  53. size_t len, plen;
  54. if (global->p2p == NULL)
  55. return 0;
  56. wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
  57. if (!global->wifi_display) {
  58. wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
  59. "include WFD IE");
  60. p2p_set_wfd_ie_beacon(global->p2p, NULL);
  61. p2p_set_wfd_ie_probe_req(global->p2p, NULL);
  62. p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
  63. p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
  64. p2p_set_wfd_ie_invitation(global->p2p, NULL);
  65. p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
  66. p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
  67. p2p_set_wfd_ie_go_neg(global->p2p, NULL);
  68. p2p_set_wfd_dev_info(global->p2p, NULL);
  69. p2p_set_wfd_r2_dev_info(global->p2p, NULL);
  70. p2p_set_wfd_assoc_bssid(global->p2p, NULL);
  71. p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
  72. return 0;
  73. }
  74. p2p_set_wfd_dev_info(global->p2p,
  75. global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
  76. p2p_set_wfd_r2_dev_info(
  77. global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
  78. p2p_set_wfd_assoc_bssid(
  79. global->p2p,
  80. global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
  81. p2p_set_wfd_coupled_sink_info(
  82. global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
  83. /*
  84. * WFD IE is included in number of management frames. Two different
  85. * sets of subelements are included depending on the frame:
  86. *
  87. * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
  88. * Provision Discovery Req:
  89. * WFD Device Info
  90. * [Associated BSSID]
  91. * [Coupled Sink Info]
  92. *
  93. * Probe Request:
  94. * WFD Device Info
  95. * [Associated BSSID]
  96. * [Coupled Sink Info]
  97. * [WFD Extended Capability]
  98. *
  99. * Probe Response:
  100. * WFD Device Info
  101. * [Associated BSSID]
  102. * [Coupled Sink Info]
  103. * [WFD Extended Capability]
  104. * [WFD Session Info]
  105. *
  106. * (Re)Association Response, P2P Invitation Req/Resp,
  107. * Provision Discovery Resp:
  108. * WFD Device Info
  109. * [Associated BSSID]
  110. * [Coupled Sink Info]
  111. * [WFD Session Info]
  112. */
  113. len = 0;
  114. if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
  115. len += wpabuf_len(global->wfd_subelem[
  116. WFD_SUBELEM_DEVICE_INFO]);
  117. if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
  118. len += wpabuf_len(global->wfd_subelem[
  119. WFD_SUBELEM_R2_DEVICE_INFO]);
  120. if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
  121. len += wpabuf_len(global->wfd_subelem[
  122. WFD_SUBELEM_ASSOCIATED_BSSID]);
  123. if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
  124. len += wpabuf_len(global->wfd_subelem[
  125. WFD_SUBELEM_COUPLED_SINK]);
  126. if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
  127. len += wpabuf_len(global->wfd_subelem[
  128. WFD_SUBELEM_SESSION_INFO]);
  129. if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
  130. len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
  131. buf = wpabuf_alloc(len);
  132. if (buf == NULL)
  133. return -1;
  134. if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
  135. wpabuf_put_buf(buf,
  136. global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
  137. if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
  138. wpabuf_put_buf(buf,
  139. global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
  140. if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
  141. wpabuf_put_buf(buf, global->wfd_subelem[
  142. WFD_SUBELEM_ASSOCIATED_BSSID]);
  143. if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
  144. wpabuf_put_buf(buf,
  145. global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
  146. ie = wifi_display_encaps(buf);
  147. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
  148. p2p_set_wfd_ie_beacon(global->p2p, ie);
  149. ie = wifi_display_encaps(buf);
  150. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
  151. ie);
  152. p2p_set_wfd_ie_assoc_req(global->p2p, ie);
  153. ie = wifi_display_encaps(buf);
  154. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
  155. p2p_set_wfd_ie_go_neg(global->p2p, ie);
  156. ie = wifi_display_encaps(buf);
  157. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
  158. "Request", ie);
  159. p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
  160. plen = buf->used;
  161. if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
  162. wpabuf_put_buf(buf,
  163. global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
  164. ie = wifi_display_encaps(buf);
  165. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
  166. p2p_set_wfd_ie_probe_req(global->p2p, ie);
  167. if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
  168. wpabuf_put_buf(buf,
  169. global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
  170. ie = wifi_display_encaps(buf);
  171. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
  172. p2p_set_wfd_ie_probe_resp(global->p2p, ie);
  173. /* Remove WFD Extended Capability from buffer */
  174. buf->used = plen;
  175. if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
  176. wpabuf_put_buf(buf,
  177. global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
  178. ie = wifi_display_encaps(buf);
  179. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
  180. p2p_set_wfd_ie_invitation(global->p2p, ie);
  181. ie = wifi_display_encaps(buf);
  182. wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
  183. "Response", ie);
  184. p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
  185. wpabuf_free(buf);
  186. return 0;
  187. }
  188. void wifi_display_enable(struct wpa_global *global, int enabled)
  189. {
  190. wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
  191. enabled ? "enabled" : "disabled");
  192. global->wifi_display = enabled;
  193. wifi_display_update_wfd_ie(global);
  194. }
  195. int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
  196. {
  197. char *pos;
  198. int subelem;
  199. size_t len;
  200. struct wpabuf *e;
  201. pos = os_strchr(cmd, ' ');
  202. if (pos == NULL)
  203. return -1;
  204. *pos++ = '\0';
  205. len = os_strlen(pos);
  206. if (len & 1)
  207. return -1;
  208. len /= 2;
  209. if (os_strcmp(cmd, "all") == 0) {
  210. int res;
  211. e = wpabuf_alloc(len);
  212. if (e == NULL)
  213. return -1;
  214. if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
  215. wpabuf_free(e);
  216. return -1;
  217. }
  218. res = wifi_display_subelem_set_from_ies(global, e);
  219. wpabuf_free(e);
  220. return res;
  221. }
  222. subelem = atoi(cmd);
  223. if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
  224. return -1;
  225. if (len == 0) {
  226. /* Clear subelement */
  227. e = NULL;
  228. wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
  229. } else {
  230. e = wpabuf_alloc(1 + len);
  231. if (e == NULL)
  232. return -1;
  233. wpabuf_put_u8(e, subelem);
  234. if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
  235. wpabuf_free(e);
  236. return -1;
  237. }
  238. wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
  239. }
  240. wpabuf_free(global->wfd_subelem[subelem]);
  241. global->wfd_subelem[subelem] = e;
  242. wifi_display_update_wfd_ie(global);
  243. return 0;
  244. }
  245. int wifi_display_subelem_set_from_ies(struct wpa_global *global,
  246. struct wpabuf *ie)
  247. {
  248. int subelements[MAX_WFD_SUBELEMS] = {};
  249. const u8 *pos, *end;
  250. unsigned int len, subelem;
  251. struct wpabuf *e;
  252. wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
  253. ie, ie ? (unsigned long) wpabuf_len(ie) : 0);
  254. if (ie == NULL || wpabuf_len(ie) < 6)
  255. return -1;
  256. pos = wpabuf_head(ie);
  257. end = pos + wpabuf_len(ie);
  258. while (end > pos) {
  259. if (pos + 3 > end)
  260. break;
  261. len = WPA_GET_BE16(pos + 1) + 3;
  262. wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
  263. *pos, len - 3);
  264. if (len > (unsigned int) (end - pos))
  265. break;
  266. subelem = *pos;
  267. if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) {
  268. e = wpabuf_alloc_copy(pos, len);
  269. if (e == NULL)
  270. return -1;
  271. wpabuf_free(global->wfd_subelem[subelem]);
  272. global->wfd_subelem[subelem] = e;
  273. subelements[subelem] = 1;
  274. }
  275. pos += len;
  276. }
  277. for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) {
  278. if (subelements[subelem] == 0) {
  279. wpabuf_free(global->wfd_subelem[subelem]);
  280. global->wfd_subelem[subelem] = NULL;
  281. }
  282. }
  283. return wifi_display_update_wfd_ie(global);
  284. }
  285. int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
  286. char *buf, size_t buflen)
  287. {
  288. int subelem;
  289. if (os_strcmp(cmd, "all") == 0) {
  290. struct wpabuf *ie;
  291. int res;
  292. ie = wifi_display_get_wfd_ie(global);
  293. if (ie == NULL)
  294. return 0;
  295. res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
  296. wpabuf_len(ie));
  297. wpabuf_free(ie);
  298. return res;
  299. }
  300. subelem = atoi(cmd);
  301. if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
  302. return -1;
  303. if (global->wfd_subelem[subelem] == NULL)
  304. return 0;
  305. return wpa_snprintf_hex(buf, buflen,
  306. wpabuf_head_u8(global->wfd_subelem[subelem]) +
  307. 1,
  308. wpabuf_len(global->wfd_subelem[subelem]) - 1);
  309. }
  310. char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
  311. {
  312. char *subelem = NULL;
  313. const u8 *buf;
  314. size_t buflen;
  315. size_t i = 0;
  316. u16 elen;
  317. if (!wfd_subelems)
  318. return NULL;
  319. buf = wpabuf_head_u8(wfd_subelems);
  320. if (!buf)
  321. return NULL;
  322. buflen = wpabuf_len(wfd_subelems);
  323. while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
  324. elen = WPA_GET_BE16(buf + i + 1);
  325. if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
  326. break; /* truncated subelement */
  327. if (buf[i] == id) {
  328. /*
  329. * Limit explicitly to an arbitrary length to avoid
  330. * unnecessarily large allocations. In practice, this
  331. * is limited to maximum frame length anyway, so the
  332. * maximum memory allocation here is not really that
  333. * large. Anyway, the Wi-Fi Display subelements that
  334. * are fetched with this function are even shorter.
  335. */
  336. if (elen > 1000)
  337. break;
  338. subelem = os_zalloc(2 * elen + 1);
  339. if (!subelem)
  340. return NULL;
  341. wpa_snprintf_hex(subelem, 2 * elen + 1,
  342. buf + i +
  343. WIFI_DISPLAY_SUBELEM_HEADER_LEN,
  344. elen);
  345. break;
  346. }
  347. i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
  348. }
  349. return subelem;
  350. }