att_db.h (fe5a6c4e963207a75641439241c8337880fdfb14) | att_db.h (c436b760549c6da73e320c4d93ff3b76282abccb) |
---|---|
1/* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright --- 80 unchanged lines hidden (view full) --- 89 90#define ATT_READ_MULTIPLE_VARIABLE_REQ 0x20 91#define ATT_READ_MULTIPLE_VARIABLE_RSP 0x21 92#define ATT_MULTIPLE_HANDLE_VALUE_NTF 0x23 93 94#define ATT_WRITE_COMMAND 0x52 95#define ATT_SIGNED_WRITE_COMMAND 0xD2 96 | 1/* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright --- 80 unchanged lines hidden (view full) --- 89 90#define ATT_READ_MULTIPLE_VARIABLE_REQ 0x20 91#define ATT_READ_MULTIPLE_VARIABLE_RSP 0x21 92#define ATT_MULTIPLE_HANDLE_VALUE_NTF 0x23 93 94#define ATT_WRITE_COMMAND 0x52 95#define ATT_SIGNED_WRITE_COMMAND 0xD2 96 |
97// map ATT ERROR CODES on to att_read_callback length 98#define ATT_READ_ERROR_CODE_OFFSET 0xfe00 | |
99 | 97 |
100// custom BTstack ATT Response Pending for att_read_callback 101#define ATT_READ_RESPONSE_PENDING 0xffff 102 103// internally used to signal write response pending 104#define ATT_INTERNAL_WRITE_RESPONSE_PENDING 0xfffe 105 | |
106// internal additions 107// 128 bit UUID used 108#define ATT_PROPERTY_UUID128 0x200 109// Read/Write Permission bits 110#define ATT_PROPERTY_READ_PERMISSION_BIT_0 0x0400 111#define ATT_PROPERTY_READ_PERMISSION_BIT_1 0x0800 112#define ATT_PROPERTY_WRITE_PERMISSION_BIT_0 0x0001 113#define ATT_PROPERTY_WRITE_PERMISSION_BIT_1 0x0010 --- 7 unchanged lines hidden (view full) --- 121 uint16_t max_mtu; // local maximal L2CAP_MTU, set to l2cap_max_le_mtu() 122 bool mtu_exchanged; 123 uint8_t encryption_key_size; 124 uint8_t authenticated; 125 uint8_t authorized; 126 uint8_t secure_connection; 127} att_connection_t; 128 | 98// internal additions 99// 128 bit UUID used 100#define ATT_PROPERTY_UUID128 0x200 101// Read/Write Permission bits 102#define ATT_PROPERTY_READ_PERMISSION_BIT_0 0x0400 103#define ATT_PROPERTY_READ_PERMISSION_BIT_1 0x0800 104#define ATT_PROPERTY_WRITE_PERMISSION_BIT_0 0x0001 105#define ATT_PROPERTY_WRITE_PERMISSION_BIT_1 0x0010 --- 7 unchanged lines hidden (view full) --- 113 uint16_t max_mtu; // local maximal L2CAP_MTU, set to l2cap_max_le_mtu() 114 bool mtu_exchanged; 115 uint8_t encryption_key_size; 116 uint8_t authenticated; 117 uint8_t authorized; 118 uint8_t secure_connection; 119} att_connection_t; 120 |
129// ATT Client Read Callback for Dynamic Data 130// - if buffer == NULL, don't copy data, just return size of value 131// - if buffer != NULL, copy data and return number bytes copied 132// If ENABLE_ATT_DELAYED_READ_RESPONSE is defined, you may return ATT_READ_RESPONSE_PENDING if data isn't available yet 133// @param con_handle of hci le connection 134// @param attribute_handle to be read 135// @param offset defines start of attribute value 136// @param buffer 137// @param buffer_size | 121/* API_START */ 122 123// map ATT ERROR CODES on to att_read_callback length 124#define ATT_READ_ERROR_CODE_OFFSET 0xfe00 125 126// custom BTstack ATT Response Pending for att_read_callback 127#define ATT_READ_RESPONSE_PENDING 0xffff 128 129// internally used to signal write response pending 130#define ATT_INTERNAL_WRITE_RESPONSE_PENDING 0xfffe 131 132/** 133 * @brief ATT Client Read Callback for Dynamic Data 134 * - if buffer == NULL, don't copy data, just return size of value 135 * - if buffer != NULL, copy data and return number bytes copied 136 * If ENABLE_ATT_DELAYED_READ_RESPONSE is defined, you may return ATT_READ_RESPONSE_PENDING if data isn't available yet 137 * @param con_handle of hci le connection 138 * @param attribute_handle to be read 139 * @param offset defines start of attribute value 140 * @param buffer 141 * @param buffer_size 142 * @return size of value if buffer is NULL, otherwise number of bytes copied 143 */ |
138typedef uint16_t (*att_read_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 139 | 144typedef uint16_t (*att_read_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 145 |
140// ATT Client Write Callback for Dynamic Data 141// @param con_handle of hci le connection 142// @param attribute_handle to be written 143// @param transaction - ATT_TRANSACTION_MODE_NONE for regular writes. For prepared writes: ATT_TRANSACTION_MODE_ACTIVE, ATT_TRANSACTION_MODE_VALIDATE, ATT_TRANSACTION_MODE_EXECUTE, ATT_TRANSACTION_MODE_CANCEL 144// @param offset into the value - used for queued writes and long attributes 145// @param buffer 146// @param buffer_size 147// @param signature used for signed write commmands 148// @returns 0 if write was ok, ATT_ERROR_PREPARE_QUEUE_FULL if no space in queue, ATT_ERROR_INVALID_OFFSET if offset is larger than max buffer 149// 150// Each Prepared Write Request triggers a callback with transaction mode ATT_TRANSACTION_MODE_ACTIVE. 151// On Execute Write, the callback will be called with ATT_TRANSACTION_MODE_VALIDATE and allows to validate all queued writes and return an application error. 152// If none of the registered callbacks return an error for ATT_TRANSACTION_MODE_VALIDATE and the callback will be called with ATT_TRANSACTION_MODE_EXECUTE. 153// Otherwise, all callbacks will be called with ATT_TRANSACTION_MODE_CANCEL. 154// 155// If the additional validation step is not needed, just return 0 for all callbacks with transaction mode ATT_TRANSACTION_MODE_VALIDATE. 156// | 146/** 147 * @brief ATT Client Write Callback for Dynamic Data 148 * Each Prepared Write Request triggers a callback with transaction mode ATT_TRANSACTION_MODE_ACTIVE. 149 * On Execute Write, the callback will be called with ATT_TRANSACTION_MODE_VALIDATE and allows to validate all queued writes and return an application error. 150 * If none of the registered callbacks return an error for ATT_TRANSACTION_MODE_VALIDATE and the callback will be called with ATT_TRANSACTION_MODE_EXECUTE. 151 * Otherwise, all callbacks will be called with ATT_TRANSACTION_MODE_CANCEL. 152 * 153 * If the additional validation step is not needed, just return 0 for all callbacks with transaction mode ATT_TRANSACTION_MODE_VALIDATE. 154 * 155 * @param con_handle of hci le connection 156 * @param attribute_handle to be written 157 * @param transaction - ATT_TRANSACTION_MODE_NONE for regular writes. For prepared writes: ATT_TRANSACTION_MODE_ACTIVE, ATT_TRANSACTION_MODE_VALIDATE, ATT_TRANSACTION_MODE_EXECUTE, ATT_TRANSACTION_MODE_CANCEL 158 * @param offset into the value - used for queued writes and long attributes 159 * @param buffer 160 * @param buffer_size 161 * @param signature used for signed write commmands 162 * @return 0 if write was ok, ATT_ERROR_PREPARE_QUEUE_FULL if no space in queue, ATT_ERROR_INVALID_OFFSET if offset is larger than max buffer 163 */ |
157typedef int (*att_write_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size); 158 159// Read & Write Callbacks for handle range 160typedef struct att_service_handler { 161 btstack_linked_item_t * item; 162 uint16_t start_handle; 163 uint16_t end_handle; 164 att_read_callback_t read_callback; 165 att_write_callback_t write_callback; 166 btstack_packet_handler_t packet_handler; 167} att_service_handler_t; 168 169// MARK: ATT Operations 170 | 164typedef int (*att_write_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size); 165 166// Read & Write Callbacks for handle range 167typedef struct att_service_handler { 168 btstack_linked_item_t * item; 169 uint16_t start_handle; 170 uint16_t end_handle; 171 att_read_callback_t read_callback; 172 att_write_callback_t write_callback; 173 btstack_packet_handler_t packet_handler; 174} att_service_handler_t; 175 176// MARK: ATT Operations 177 |
171/* | 178/** |
172 * @brief setup ATT database | 179 * @brief setup ATT database |
180 * @param db |
|
173 */ 174void att_set_db(uint8_t const * db); 175 176/* 177 * @brief set callback for read of dynamic attributes 178 * @param callback 179 */ 180void att_set_read_callback(att_read_callback_t callback); 181 | 181 */ 182void att_set_db(uint8_t const * db); 183 184/* 185 * @brief set callback for read of dynamic attributes 186 * @param callback 187 */ 188void att_set_read_callback(att_read_callback_t callback); 189 |
182/* | 190/** |
183 * @brief set callback for write of dynamic attributes 184 * @param callback 185 */ 186void att_set_write_callback(att_write_callback_t callback); 187 | 191 * @brief set callback for write of dynamic attributes 192 * @param callback 193 */ 194void att_set_write_callback(att_write_callback_t callback); 195 |
188/* | 196/** |
189 * @brief debug helper, dump ATT database to stdout using log_info 190 */ 191void att_dump_attributes(void); 192 | 197 * @brief debug helper, dump ATT database to stdout using log_info 198 */ 199void att_dump_attributes(void); 200 |
193/* | 201/** |
194 * @brief process ATT request against database and put response into response buffer 195 * @param att_connection used for mtu and security properties 196 * @param request_buffer, request_len: ATT request from clinet 197 * @param response_buffer for result | 202 * @brief process ATT request against database and put response into response buffer 203 * @param att_connection used for mtu and security properties 204 * @param request_buffer, request_len: ATT request from clinet 205 * @param response_buffer for result |
198 * @returns len of data in response buffer. 0 = no response, | 206 * @return len of data in response buffer. 0 = no response, |
199 * ATT_READ_RESPONSE_PENDING if it was returned at least once for dynamic data (requires ENABLE_ATT_DELAYED_READ_RESPONSE) 200 */ 201uint16_t att_handle_request(att_connection_t * att_connection, 202 uint8_t * request_buffer, 203 uint16_t request_len, 204 uint8_t * response_buffer); 205 | 207 * ATT_READ_RESPONSE_PENDING if it was returned at least once for dynamic data (requires ENABLE_ATT_DELAYED_READ_RESPONSE) 208 */ 209uint16_t att_handle_request(att_connection_t * att_connection, 210 uint8_t * request_buffer, 211 uint16_t request_len, 212 uint8_t * response_buffer); 213 |
206/* | 214/** |
207 * @brief setup value notification in response buffer for a given handle and value 208 * @param att_connection 209 * @param attribute_handle 210 * @param value 211 * @param value_len 212 * @param response_buffer for notification 213 */ 214uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection, 215 uint16_t attribute_handle, 216 const uint8_t *value, 217 uint16_t value_len, 218 uint8_t * response_buffer); 219 | 215 * @brief setup value notification in response buffer for a given handle and value 216 * @param att_connection 217 * @param attribute_handle 218 * @param value 219 * @param value_len 220 * @param response_buffer for notification 221 */ 222uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection, 223 uint16_t attribute_handle, 224 const uint8_t *value, 225 uint16_t value_len, 226 uint8_t * response_buffer); 227 |
220/* | 228/** |
221 * @brief setup value indication in response buffer for a given handle and value 222 * @param att_connection 223 * @param attribute_handle 224 * @param value 225 * @param value_len 226 * @param response_buffer for indication 227 */ 228uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection, 229 uint16_t attribute_handle, 230 const uint8_t *value, 231 uint16_t value_len, 232 uint8_t * response_buffer); 233 | 229 * @brief setup value indication in response buffer for a given handle and value 230 * @param att_connection 231 * @param attribute_handle 232 * @param value 233 * @param value_len 234 * @param response_buffer for indication 235 */ 236uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection, 237 uint16_t attribute_handle, 238 const uint8_t *value, 239 uint16_t value_len, 240 uint8_t * response_buffer); 241 |
234/* | 242/** |
235 * @brief transcation queue of prepared writes, e.g., after disconnect | 243 * @brief transcation queue of prepared writes, e.g., after disconnect |
244 * @return att_connection |
|
236 */ 237void att_clear_transaction_queue(att_connection_t * att_connection); 238 239// att_read_callback helpers for a various data types 240 | 245 */ 246void att_clear_transaction_queue(att_connection_t * att_connection); 247 248// att_read_callback helpers for a various data types 249 |
241/* | 250/** |
242 * @brief Handle read of blob like data for att_read_callback 243 * @param blob of data 244 * @param blob_size of blob 245 * @param offset from att_read_callback 246 * @param buffer from att_read_callback 247 * @param buffer_size from att_read_callback | 251 * @brief Handle read of blob like data for att_read_callback 252 * @param blob of data 253 * @param blob_size of blob 254 * @param offset from att_read_callback 255 * @param buffer from att_read_callback 256 * @param buffer_size from att_read_callback |
248 * @returns value size for buffer == 0 and num bytes copied otherwise | 257 * @return value size for buffer == 0 and num bytes copied otherwise |
249 */ 250uint16_t att_read_callback_handle_blob(const uint8_t * blob, uint16_t blob_size, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 251 | 258 */ 259uint16_t att_read_callback_handle_blob(const uint8_t * blob, uint16_t blob_size, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 260 |
252/* | 261/** |
253 * @brief Handle read of little endian unsigned 32 bit value for att_read_callback 254 * @param value 255 * @param offset from att_read_callback 256 * @param buffer from att_read_callback 257 * @param buffer_size from att_read_callback | 262 * @brief Handle read of little endian unsigned 32 bit value for att_read_callback 263 * @param value 264 * @param offset from att_read_callback 265 * @param buffer from att_read_callback 266 * @param buffer_size from att_read_callback |
258 * @returns value size for buffer == 0 and num bytes copied otherwise | 267 * @return value size for buffer == 0 and num bytes copied otherwise |
259 */ 260uint16_t att_read_callback_handle_little_endian_32(uint32_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 261 | 268 */ 269uint16_t att_read_callback_handle_little_endian_32(uint32_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 270 |
262/* | 271/** |
263 * @brief Handle read of little endian unsigned 16 bit value for att_read_callback 264 * @param value 265 * @param offset from att_read_callback 266 * @param buffer from att_read_callback 267 * @param buffer_size from att_read_callback | 272 * @brief Handle read of little endian unsigned 16 bit value for att_read_callback 273 * @param value 274 * @param offset from att_read_callback 275 * @param buffer from att_read_callback 276 * @param buffer_size from att_read_callback |
268 * @returns value size for buffer == 0 and num bytes copied otherwise | 277 * @return value size for buffer == 0 and num bytes copied otherwise |
269 */ 270uint16_t att_read_callback_handle_little_endian_16(uint16_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 271 | 278 */ 279uint16_t att_read_callback_handle_little_endian_16(uint16_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 280 |
272/* | 281/** |
273 * @brief Handle read of single byte for att_read_callback 274 * @param blob of data 275 * @param blob_size of blob 276 * @param offset from att_read_callback 277 * @param buffer from att_read_callback 278 * @param buffer_size from att_read_callback | 282 * @brief Handle read of single byte for att_read_callback 283 * @param blob of data 284 * @param blob_size of blob 285 * @param offset from att_read_callback 286 * @param buffer from att_read_callback 287 * @param buffer_size from att_read_callback |
279 * @returns value size for buffer == 0 and num bytes copied otherwise | 288 * @return value size for buffer == 0 and num bytes copied otherwise |
280 */ 281uint16_t att_read_callback_handle_byte(uint8_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 282 283 284// experimental client API | 289 */ 290uint16_t att_read_callback_handle_byte(uint8_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); 291 292 293// experimental client API |
294/** 295 * @brief Get UUID for handle 296 * @param attribute_handle 297 * @return 0 if not found 298 */ |
|
285uint16_t att_uuid_for_handle(uint16_t attribute_handle); 286 | 299uint16_t att_uuid_for_handle(uint16_t attribute_handle); 300 |
287 | |
288// experimental GATT Server API 289 | 301// experimental GATT Server API 302 |
290// returns true if service found. only primary service. 291bool gatt_server_get_get_handle_range_for_service_with_uuid16(uint16_t uuid16, uint16_t * start_handle, uint16_t * end_handle); | 303/** 304 * @brief Get handle range for primary service. 305 * @param uuid16 306 * @param start_handle 307 * @param end_handle 308 * @return false if not found 309 */ 310bool gatt_server_get_handle_range_for_service_with_uuid16(uint16_t uuid16, uint16_t * start_handle, uint16_t * end_handle); |
292 | 311 |
293// returns 0 if not found | 312/** 313 * @brief Get value handle for characteristic. 314 * @param start_handle 315 * @param end_handle 316 * @param uuid16 317 * @return 0 if not found 318 */ |
294uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16); 295 | 319uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16); 320 |
296// returns 0 if not found | 321/** 322 * @brief Get descriptor handle for characteristic. 323 * @param start_handle 324 * @param end_handle 325 * @param characteristic_uuid16 326 * @param descriptor_uuid16 327 * @return 0 if not found 328 */ |
297uint16_t gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16, uint16_t descriptor_uuid16); | 329uint16_t gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16, uint16_t descriptor_uuid16); |
330 331/** 332 * @brief Get client configuration handle for characteristic. 333 * @param start_handle 334 * @param end_handle 335 * @param characteristic_uuid16 336 * @return 0 if not found 337 */ |
|
298uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16); | 338uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16); |
339 340/** 341 * @brief Get server configuration handle for characteristic. 342 * @param start_handle 343 * @param end_handle 344 * @param characteristic_uuid16 345 * @param descriptor_uuid16 346 * @return 0 if not found 347 */ |
|
299uint16_t gatt_server_get_server_configuration_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16); 300 | 348uint16_t gatt_server_get_server_configuration_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16); 349 |
350/** 351 * @brief Get handle range for primary service. 352 * @param uuid128 353 * @param start_handle 354 * @param end_handle 355 * @return 0 if not found 356 */ 357int gatt_server_get_handle_range_for_service_with_uuid128(const uint8_t * uuid128, uint16_t * start_handle, uint16_t * end_handle); |
|
301 | 358 |
302// returns 1 if service found. only primary service. 303int gatt_server_get_get_handle_range_for_service_with_uuid128(const uint8_t * uuid128, uint16_t * start_handle, uint16_t * end_handle); 304 305// returns 0 if not found | 359/** 360 * @brief Get value handle. 361 * @param start_handle 362 * @param end_handle 363 * @param uuid128 364 * @return 0 if not found 365 */ |
306uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128); 307 | 366uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128); 367 |
308// returns 0 if not found | 368/** 369 * @brief Get client configuration handle. 370 * @param start_handle 371 * @param end_handle 372 * @param uuid128 373 * @return 0 if not found 374 */ |
309uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128); 310 | 375uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128); 376 |
377/* API_END */ 378 |
|
311// non-user functionality for att_server 312 | 379// non-user functionality for att_server 380 |
313/* | 381/** |
314 * @brief Check if writes to handle should be persistent 315 * @param handle | 382 * @brief Check if writes to handle should be persistent 383 * @param handle |
316 * @returns 1 if persistent | 384 * @return 1 if persistent |
317 */ 318bool att_is_persistent_ccc(uint16_t handle); 319 | 385 */ 386bool att_is_persistent_ccc(uint16_t handle); 387 |
388 389 |
|
320// auto-pts testing, returns response size 321#ifdef ENABLE_BTP 322uint16_t btp_att_get_attributes_by_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16, uint8_t * response_buffer, uint16_t response_buffer_size); 323uint16_t btp_att_get_attributes_by_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128, uint8_t * response_buffer, uint16_t response_buffer_size); 324uint16_t btp_att_get_attribute_value(att_connection_t * att_connection, uint16_t attribute_handle, uint8_t * response_buffer, uint16_t response_buffer_size); 325#endif 326 327#if defined __cplusplus 328} 329#endif 330 331#endif // ATT_H | 390// auto-pts testing, returns response size 391#ifdef ENABLE_BTP 392uint16_t btp_att_get_attributes_by_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16, uint8_t * response_buffer, uint16_t response_buffer_size); 393uint16_t btp_att_get_attributes_by_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128, uint8_t * response_buffer, uint16_t response_buffer_size); 394uint16_t btp_att_get_attribute_value(att_connection_t * att_connection, uint16_t attribute_handle, uint8_t * response_buffer, uint16_t response_buffer_size); 395#endif 396 397#if defined __cplusplus 398} 399#endif 400 401#endif // ATT_H |