diff --git a/test/avdtp/avdtp.h b/test/avdtp/avdtp.h index d3e7dc02e..da30420bd 100644 --- a/test/avdtp/avdtp.h +++ b/test/avdtp/avdtp.h @@ -79,6 +79,80 @@ extern "C" { #define AVDTP_SINK_SF_Recorder 0x0004 #define AVDTP_SINK_SF_Amplifier 0x0008 +// ACP to INT, Signal Response Header Error Codes +#define BAD_HEADER_FORMAT 0x01 + +// ACP to INT, Signal Response Payload Format Error Codes +#define BAD_LENGTH 0x11 +#define BAD_ACP_SEID 0x12 +#define SEP_IN_USE 0x13 +#define SEP_NOT_IN_USE 0x14 +#define BAD_SERV_CATEGORY 0x17 +#define BAD_PAYLOAD_FORMAT 0x18 +#define NOT_SUPPORTED_COMMAND 0x19 +#define INVALID_CAPABILITIES 0x1A + +// ACP to INT, Signal Response Transport Service Capabilities Error Codes +#define BAD_RECOVERY_TYPE 0x22 +#define BAD_MEDIA_TRANSPORT_FORMAT 0x23 +#define BAD_RECOVERY_FORMAT 0x25 +#define BAD_ROHC_FORMAT 0x26 +#define BAD_CP_FORMAT 0x27 +#define BAD_MULTIPLEXING_FORMAT 0x28 +#define UNSUPPORTED_CONFIGURAION 0x29 + +// ACP to INT, Procedure Error Codes +#define BAD_STATE 0x31 +// Signal Identifier fields +typedef enum { + AVDTP_DISCOVER = 0x01, + AVDTP_GET_CAPABILITIES, + AVDTP_SET_CONFIGURATION, + AVDTP_GET_CONFIGURATION, + AVDTP_RECONFIGURE, + AVDTP_OPEN, + AVDTP_START, + AVDTP_CLOSE, + AVDTP_SUSPEND, + AVDTP_ABORT, + AVDTP_SECURITY_CONTROL, + AVDTP_GET_ALL_CAPABILITIES, + AVDTP_DELAYREPORT +} avdtp_signal_identifier_t; + +typedef enum { + AVDTP_SINGLE_PACKET= 0, + AVDTP_START_PACKET , + AVDTP_CONTINUE_PACKET , + AVDTP_END_PACKET +} avdtp_packet_type_t; + +typedef enum { + AVDTP_CMD_MSG = 0, + AVDTP_GENERAL_REJECT_MSG , + AVDTP_RESPONSE_ACCEPT_MSG , + AVDTP_RESPONSE_REJECT_MSG +} avdtp_message_type_t; + +typedef enum{ + AVDTP_AUDIO = 0, + AVDTP_VIDEO, + AVDTP_MULTIMEDIA +} avdtp_media_type_t; + +typedef enum{ + AVDTP_SOURCE = 0, + AVDTP_SINK +} avdtp_sep_type_t; + +typedef struct { + uint8_t seid; // 0x01 – 0x3E, 6bit + uint8_t in_use; // 1 bit, 0 - not in use, 1 - in use + avdtp_media_type_t media_type; // 4 bit + avdtp_sep_type_t type; // 1 bit, 0 - SRC, 1 - SNK +} avdtp_sep_t; + + #if defined __cplusplus } #endif diff --git a/test/avdtp/avdtp_sink.c b/test/avdtp/avdtp_sink.c index edab58dcd..de702c430 100644 --- a/test/avdtp/avdtp_sink.c +++ b/test/avdtp/avdtp_sink.c @@ -51,6 +51,8 @@ static const char * default_avdtp_sink_service_provider_name = "BTstack AVDTP Si static btstack_linked_list_t avdtp_sink_connections = NULL; +static uint8_t transaction_label = 0; + static btstack_packet_handler_t avdtp_sink_callback; static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void avdtp_sink_run_for_connection(avdtp_sink_connection_t *connection); @@ -138,8 +140,6 @@ void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_han static avdtp_sink_connection_t * create_avdtp_sink_connection_context(bd_addr_t bd_addr){ avdtp_sink_connection_t * avdtp_connection = btstack_memory_avdtp_sink_connection_get(); if (!avdtp_connection) return NULL; - printf("create_avdtp_sink_connection_context %p\n", avdtp_connection); - memset(avdtp_connection,0, sizeof(avdtp_sink_connection_t)); memcpy(avdtp_connection->remote_addr, bd_addr, 6); avdtp_connection->state = AVDTP_SINK_IDLE; @@ -172,10 +172,77 @@ static avdtp_sink_connection_t * get_avdtp_sink_connection_context_for_l2cap_cid return NULL; } -static void remove_avdtp_sink_connection_context(avdtp_sink_connection_t * connection){ +static void avdtp_sink_remove_connection_context(avdtp_sink_connection_t * connection){ btstack_linked_list_remove(&avdtp_sink_connections, (btstack_linked_item_t*) connection); } +static inline uint8_t avdtp_header(uint8_t tr_label, avdtp_packet_type_t packet_type, avdtp_message_type_t msg_type){ + return (tr_label<<4) | ((uint8_t)packet_type<<2) | (uint8_t)msg_type; +} + +static int avdtp_sink_send_discover_cmd(uint16_t cid){ + uint8_t command[2]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_CMD_MSG); + command[1] = (uint8_t)AVDTP_DISCOVER; + return l2cap_send(cid, command, sizeof(command)); +} + +static int avdtp_sink_send_get_capabilities_cmd(uint16_t cid, uint8_t sep_id){ + return 0; +} + +static void handle_l2cap_data_packet(avdtp_sink_connection_t * connection, uint8_t *packet, uint16_t size){ + if (size < 2) { + log_error("l2cap data packet too small"); + return; + } + uint8_t tr_label = packet[0] >> 4; + avdtp_packet_type_t packet_type = (avdtp_packet_type_t)((packet[0] >> 2) & 0x03); + avdtp_message_type_t msg_type = (avdtp_message_type_t) (packet[0] & 0x03); + uint8_t signal_identifier = packet[1] & 0x3f; + + printf("\nheader: tr_label %d, packet_type %d, msg_type %d, signal_identifier %02x\n", tr_label, packet_type, msg_type, signal_identifier); + + int i = 2; + avdtp_sep_t sep; + + switch (connection->state){ + case AVDTP_SINK_W4_SEPS_DISCOVERED: + if (tr_label != transaction_label){ + log_error("unexpected transaction label, got %d, expected %d", tr_label, transaction_label); + return; + } + if (signal_identifier != AVDTP_DISCOVER) { + log_error("unexpected signal identifier ..."); + return; + } + if (size == 3){ + printf("ERROR code %02x\n", packet[2]); + return; + } + for (i = 2; i> 2; + if (sep.seid < 0x01 || sep.seid > 0x3E){ + log_error("invalid sep id"); + return; + } + sep.in_use = (packet[i] >> 1) & 0x01; + sep.media_type = (avdtp_media_type_t)(packet[i+1] >> 4); + sep.type = (avdtp_sep_type_t)((packet[i+1] >> 3) & 0x01); + connection->remote_seps[connection->remote_seps_num++] = sep; + printf("found sep: seid %u, in_use %d, media type %d, sep type %d (1-SNK)\n", + sep.seid, sep.in_use, sep.media_type, sep.type); + } + // connection->state = AVDTP_SINK_W2_GET_CAPABILITIES; + // transaction_label++; + // l2cap_request_can_send_now_event(connection->l2cap_cid); + break; + default: + printf_hexdump( packet, size ); + break; + } +} + static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ bd_addr_t event_addr; hci_con_handle_t con_handle; @@ -183,14 +250,15 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe uint16_t remote_cid; uint16_t local_cid; avdtp_sink_connection_t * connection = NULL; - - printf("avdtp_sink packet handler: Received packet type %02x\n", packet_type); - + switch (packet_type) { case L2CAP_DATA_PACKET: - // just dump data for now - printf("source cid %x -- ", channel); - // printf_hexdump( packet, size ); + connection = get_avdtp_sink_connection_context_for_l2cap_cid(channel); + if (!connection){ + log_error("avdtp packet handler L2CAP_EVENT_INCOMING_CONNECTION: connection for local cid 0x%02x not found", channel); + break; + } + handle_l2cap_data_packet(connection, packet, size); break; case HCI_EVENT_PACKET: @@ -198,11 +266,11 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe case L2CAP_EVENT_INCOMING_CONNECTION: l2cap_event_incoming_connection_get_address(packet, event_addr); connection = get_avdtp_sink_connection_context_for_bd_addr(event_addr); - if (connection){ - log_error(""); + if (!connection){ + log_error("avdtp packet handler L2CAP_EVENT_INCOMING_CONNECTION: connection for bd address %s not found", bd_addr_to_str(event_addr)); break; } - + connection = create_avdtp_sink_connection_context(event_addr); if (!connection) return; @@ -233,11 +301,14 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe connection->l2cap_cid = l2cap_event_channel_opened_get_local_cid(packet); connection->acl_handle = l2cap_event_channel_opened_get_handle(packet); - printf("Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n", - bd_addr_to_str(event_addr), connection->acl_handle, psm, connection->l2cap_cid, l2cap_event_channel_opened_get_remote_cid(packet)); + printf("Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x, context %p\n", + bd_addr_to_str(event_addr), connection->acl_handle, psm, connection->l2cap_cid, l2cap_event_channel_opened_get_remote_cid(packet), + connection); - connection->state = AVDTP_SINK_W4_CONFIGURATION_COMPLETE; + connection->state = AVDTP_SINK_W2_DISCOVER_SEPS; + transaction_label++; + l2cap_request_can_send_now_event(connection->l2cap_cid); break; case L2CAP_EVENT_CHANNEL_CLOSED: @@ -247,7 +318,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe if (!connection || connection->state != AVDTP_SINK_W4_L2CAP_DISCONNECTED) return; log_info("L2CAP_EVENT_CHANNEL_CLOSED cid 0x%0x", local_cid); - remove_avdtp_sink_connection_context(connection); + avdtp_sink_remove_connection_context(connection); connection->state = AVDTP_SINK_IDLE; break; @@ -284,29 +355,42 @@ static void avdtp_sink_run_for_connection(avdtp_sink_connection_t *connection){ if (connection->release_l2cap_connection){ if (connection->state > AVDTP_SINK_W4_L2CAP_CONNECTED || connection->state < AVDTP_SINK_W4_L2CAP_DISCONNECTED){ - connection->release_l2cap_connection = 0; - connection->state = AVDTP_SINK_W4_L2CAP_DISCONNECTED; - l2cap_disconnect(connection->l2cap_cid, 0); + + connection->release_l2cap_connection = 0; + connection->state = AVDTP_SINK_W4_L2CAP_DISCONNECTED; + l2cap_disconnect(connection->l2cap_cid, 0); return; } } - // if (!l2cap_can_send_packet_now(connection->l2cap_cid)) { - // log_info("avdtp_sink_run_for_connection: request cannot send for 0x%02x", connection->l2cap_cid); - // return; - // } - + if (!l2cap_can_send_packet_now(connection->l2cap_cid)) { + log_info("avdtp_sink_run_for_connection: request cannot send for 0x%02x", connection->l2cap_cid); + return; + } + + switch (connection->state){ + case AVDTP_SINK_W2_DISCOVER_SEPS: + avdtp_sink_send_discover_cmd(connection->l2cap_cid); + connection->state = AVDTP_SINK_W4_SEPS_DISCOVERED; + break; + case AVDTP_SINK_W2_GET_CAPABILITIES: + avdtp_sink_send_get_capabilities_cmd(connection->l2cap_cid, 0); + connection->state = AVDTP_SINK_W4_CAPABILITIES; + break; + default: + break; + } } void avdtp_sink_connect(bd_addr_t bd_addr){ avdtp_sink_connection_t * connection = get_avdtp_sink_connection_context_for_bd_addr(bd_addr); if (connection) { - log_error("..."); + log_error("avdtp_sink_connect: connection for bd address %s not found", bd_addr_to_str(bd_addr)); return; } connection = create_avdtp_sink_connection_context(bd_addr); if (!connection){ - log_error(""); + log_error("avdtp_sink_connect: cannot create connection for bd address %s", bd_addr_to_str(bd_addr)); return; } @@ -314,11 +398,10 @@ void avdtp_sink_connect(bd_addr_t bd_addr){ l2cap_create_channel(packet_handler, connection->remote_addr, PSM_AVDTP, 0xffff, NULL); } - void avdtp_sink_disconnect(uint16_t l2cap_cid){ avdtp_sink_connection_t * connection = get_avdtp_sink_connection_context_for_l2cap_cid(l2cap_cid); if (!connection) { - log_error("avdtp_sink_disconnect Connection with l2cap_cid %d not found", l2cap_cid); + log_error("avdtp_sink_disconnect: Connection with l2cap_cid %d not found", l2cap_cid); return; } @@ -328,3 +411,5 @@ void avdtp_sink_disconnect(uint16_t l2cap_cid){ connection->release_l2cap_connection = 1; avdtp_sink_run_for_connection(connection); } + + diff --git a/test/avdtp/avdtp_sink.h b/test/avdtp/avdtp_sink.h index 6ef411767..6c6b0ae5a 100644 --- a/test/avdtp/avdtp_sink.h +++ b/test/avdtp/avdtp_sink.h @@ -48,6 +48,7 @@ #include #include "hci.h" +#include "avdtp.h" #if defined __cplusplus extern "C" { @@ -56,8 +57,16 @@ extern "C" { typedef enum { AVDTP_SINK_IDLE, AVDTP_SINK_W4_L2CAP_CONNECTED, - AVDTP_SINK_W4_CONFIGURATION_COMPLETE, + AVDTP_SINK_W2_DISCOVER_SEPS, + AVDTP_SINK_W4_SEPS_DISCOVERED, + AVDTP_SINK_W2_GET_CAPABILITIES, + AVDTP_SINK_W4_CAPABILITIES, + AVDTP_SINK_CONFIGURED, + AVDTP_SINK_OPEN, + AVDTP_SINK_STREAMING, + AVDTP_SINK_CLOSING, + AVDTP_SINK_ABORTING, AVDTP_SINK_W4_L2CAP_DISCONNECTED } avdtp_sink_state_t; @@ -70,6 +79,10 @@ typedef struct avdtp_sink_connection { avdtp_sink_state_t state; + avdtp_sep_t remote_seps[5]; + uint8_t remote_seps_num; + uint8_t remote_sep_index_get_capabilities; + uint8_t release_l2cap_connection; } avdtp_sink_connection_t; @@ -105,7 +118,7 @@ void avdtp_sink_connect(bd_addr_t bd_addr); /** * @brief Disconnect from device with connection handle. - * @param acl_handle + * @param l2cap_cid */ void avdtp_sink_disconnect(uint16_t l2cap_cid); diff --git a/test/avdtp/avdtp_test.c b/test/avdtp/avdtp_test.c index cf9535a01..65bd706da 100644 --- a/test/avdtp/avdtp_test.c +++ b/test/avdtp/avdtp_test.c @@ -123,7 +123,7 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac break; case 'd': printf("L2CAP Channel Closed\n"); - // l2cap_disconnect(local_cid, 0); + // avdtp_sink_disconnect(local_cid); break; case '\n': case '\r': diff --git a/test/sbc/Makefile b/test/sbc/Makefile index 25686823a..337ebe226 100644 --- a/test/sbc/Makefile +++ b/test/sbc/Makefile @@ -47,7 +47,7 @@ sbc_encoder_test: ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${COMMON_OBJ} sbc_encode ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ test: all - ./sbc_decoder_test data/sine-4sb-mono.sbc data/sine-4sb-decoded-mono.wav + ./sbc_decoder_test data/sine-4sb-mono msbc 1 100 ./sbc_encoder_test data/sine-mono.wav data/sine-4sb-mono.sbc pytest-sine: @@ -61,17 +61,6 @@ pytest-sine: ./sbc_encoder_test.py data/sine-stereo.wav 16 4 30 1 2 data/sine-4sb-stereo.sbc ./sbc_encoder_test.py data/sine-stereo.wav 16 8 64 2 data/sine-8sb-stereo.sbc -pytest-short: - ./sbc_decoder_test.py data/fanfare-short-4sb-mono.sbc 0 SIG data/fanfare-short-4sb-decoded-mono.wav - ./sbc_decoder_test.py data/fanfare-short-8sb-mono.sbc 0 V1 data/fanfare-short-8sb-decoded-mono.wav - ./sbc_decoder_test.py data/fanfare-short-4sb-stereo.sbc 2 SIG data/fanfare-short-4sb-decoded-stereo.wav - ./sbc_decoder_test.py data/fanfare-short-8sb-stereo.sbc 2 SIG data/fanfare-short-8sb-decoded-stereo.wav - - ./sbc_encoder_test.py data/fanfare-short-mono.wav 16 4 31 1 0 data/fanfare-short-4sb-mono.sbc - ./sbc_encoder_test.py data/fanfare-short-mono.wav 16 8 64 1 0 data/fanfare-short-8sb-mono.sbc - ./sbc_encoder_test.py data/fanfare-short-stereo.wav 16 4 30 1 2 data/fanfare-short-4sb-stereo.sbc - ./sbc_encoder_test.py data/fanfare-short-stereo.wav 16 8 62 1 2 data/fanfare-short-8sb-stereo.sbc - pytest: ./sbc_decoder_test.py data/fanfare-4sb-mono.sbc data/fanfare-4sb-decoded-mono.wav ./sbc_decoder_test.py data/fanfare-8sb-mono.sbc data/fanfare-8sb-decoded-mono.wav diff --git a/test/sbc/data/fanfare-4sb-decoded-stereo.wav b/test/sbc/data/fanfare-4sb-decoded-stereo.wav deleted file mode 100644 index d1b5ddbff..000000000 Binary files a/test/sbc/data/fanfare-4sb-decoded-stereo.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-8sb-decoded-mono.wav b/test/sbc/data/fanfare-8sb-decoded-mono.wav deleted file mode 100644 index 9a24d5b6b..000000000 Binary files a/test/sbc/data/fanfare-8sb-decoded-mono.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-8sb-decoded-stereo.wav b/test/sbc/data/fanfare-8sb-decoded-stereo.wav deleted file mode 100644 index 2aa7675d9..000000000 Binary files a/test/sbc/data/fanfare-8sb-decoded-stereo.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-short-4sb-decoded-mono.wav b/test/sbc/data/fanfare-short-4sb-decoded-mono.wav deleted file mode 100644 index f9a6c1c54..000000000 Binary files a/test/sbc/data/fanfare-short-4sb-decoded-mono.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-short-4sb-decoded-stereo.wav b/test/sbc/data/fanfare-short-4sb-decoded-stereo.wav deleted file mode 100644 index 3417ad5de..000000000 Binary files a/test/sbc/data/fanfare-short-4sb-decoded-stereo.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-short-4sb-mono.sbc b/test/sbc/data/fanfare-short-4sb-mono.sbc deleted file mode 100644 index d8734cc90..000000000 Binary files a/test/sbc/data/fanfare-short-4sb-mono.sbc and /dev/null differ diff --git a/test/sbc/data/fanfare-short-4sb-stereo.sbc b/test/sbc/data/fanfare-short-4sb-stereo.sbc deleted file mode 100644 index 48b68adc8..000000000 Binary files a/test/sbc/data/fanfare-short-4sb-stereo.sbc and /dev/null differ diff --git a/test/sbc/data/fanfare-short-8sb-decoded-mono.wav b/test/sbc/data/fanfare-short-8sb-decoded-mono.wav deleted file mode 100644 index 42072e60e..000000000 Binary files a/test/sbc/data/fanfare-short-8sb-decoded-mono.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-short-8sb-decoded-stereo.wav b/test/sbc/data/fanfare-short-8sb-decoded-stereo.wav deleted file mode 100644 index d832b6e22..000000000 Binary files a/test/sbc/data/fanfare-short-8sb-decoded-stereo.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-short-8sb-mono.sbc b/test/sbc/data/fanfare-short-8sb-mono.sbc deleted file mode 100644 index b59855a10..000000000 Binary files a/test/sbc/data/fanfare-short-8sb-mono.sbc and /dev/null differ diff --git a/test/sbc/data/fanfare-short-8sb-stereo.sbc b/test/sbc/data/fanfare-short-8sb-stereo.sbc deleted file mode 100644 index 058d1c526..000000000 Binary files a/test/sbc/data/fanfare-short-8sb-stereo.sbc and /dev/null differ diff --git a/test/sbc/data/fanfare-short-mono.wav b/test/sbc/data/fanfare-short-mono.wav deleted file mode 100644 index 06ec83f8f..000000000 Binary files a/test/sbc/data/fanfare-short-mono.wav and /dev/null differ diff --git a/test/sbc/data/fanfare-short-stereo.wav b/test/sbc/data/fanfare-short-stereo.wav deleted file mode 100644 index 7586cf88d..000000000 Binary files a/test/sbc/data/fanfare-short-stereo.wav and /dev/null differ diff --git a/test/sbc/data/fanfare_test-8khz.wav b/test/sbc/data/fanfare_test-8khz.wav new file mode 100644 index 000000000..cfa6ade33 Binary files /dev/null and b/test/sbc/data/fanfare_test-8khz.wav differ diff --git a/test/sbc/data/sawtooth-mono.wav b/test/sbc/data/sawtooth-mono.wav deleted file mode 100644 index 71edec551..000000000 Binary files a/test/sbc/data/sawtooth-mono.wav and /dev/null differ diff --git a/test/sbc/data/sine-4sb-decoded-stereo.wav b/test/sbc/data/sine-4sb-decoded-stereo.wav deleted file mode 100644 index 9a76adf46..000000000 Binary files a/test/sbc/data/sine-4sb-decoded-stereo.wav and /dev/null differ diff --git a/test/sbc/data/sine-4sb-mono.sbc b/test/sbc/data/sine-4sb-mono.sbc index 92a9ec6c0..e69de29bb 100644 Binary files a/test/sbc/data/sine-4sb-mono.sbc and b/test/sbc/data/sine-4sb-mono.sbc differ diff --git a/test/sbc/data/sine-8sb-decoded-mono.wav b/test/sbc/data/sine-8sb-decoded-mono.wav deleted file mode 100644 index be553a767..000000000 Binary files a/test/sbc/data/sine-8sb-decoded-mono.wav and /dev/null differ diff --git a/test/sbc/data/sine-8sb-decoded-stereo.wav b/test/sbc/data/sine-8sb-decoded-stereo.wav deleted file mode 100644 index bd1e80f3f..000000000 Binary files a/test/sbc/data/sine-8sb-decoded-stereo.wav and /dev/null differ diff --git a/test/sbc/data/sine.msbc b/test/sbc/data/sine.msbc deleted file mode 100644 index de7d31017..000000000 Binary files a/test/sbc/data/sine.msbc and /dev/null differ diff --git a/test/sbc/data/square-mono.wav b/test/sbc/data/square-mono.wav deleted file mode 100644 index 25a1ecbe4..000000000 Binary files a/test/sbc/data/square-mono.wav and /dev/null differ diff --git a/test/sbc/data/testfile.msbc b/test/sbc/data/testfile.msbc deleted file mode 100644 index a41340b85..000000000 Binary files a/test/sbc/data/testfile.msbc and /dev/null differ