OSDN Git Service

eclair snapshot
authorJean-Baptiste Queru <jbq@google.com>
Fri, 13 Nov 2009 02:46:23 +0000 (18:46 -0800)
committerJean-Baptiste Queru <jbq@google.com>
Fri, 13 Nov 2009 02:46:23 +0000 (18:46 -0800)
21 files changed:
bluedroid/Android.mk
bluedroid/bluetooth.c
bluedroid/include/bluedroid/bluetooth.h
bluez-clean-headers/bluetooth/hci.h
bluez-clean-headers/bluetooth/l2cap.h [new file with mode: 0644]
brcm_patchram_plus/Android.mk [new file with mode: 0644]
brcm_patchram_plus/brcm_patchram_plus.c [new file with mode: 0644]
data/Android.mk
data/audio.conf
data/hcid.conf [deleted file]
data/input.conf
data/main.conf [new file with mode: 0644]
tools/Android.mk [new file with mode: 0644]
tools/asocket_test.c [new file with mode: 0644]
tools/bttest.c [moved from bluedroid/bttest.c with 100% similarity]
tools/pipetest.c [new file with mode: 0644]
tools/sock_shutdown_bug_l2cap.c [new file with mode: 0644]
tools/sock_shutdown_bug_rfcomm.c [new file with mode: 0644]
tools/sock_shutdown_bug_tcp.c [new file with mode: 0644]
tools/sock_shutdown_test.c [new file with mode: 0644]
tools/socktest.c [new file with mode: 0644]

index b4f8a39..17df49b 100644 (file)
@@ -19,22 +19,3 @@ LOCAL_SHARED_LIBRARIES := \
 LOCAL_MODULE := libbluedroid
 
 include $(BUILD_SHARED_LIBRARY)
-
-
-#
-# bttest
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := bttest.c
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-LOCAL_SHARED_LIBRARIES := libbluedroid
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := eng
-LOCAL_MODULE := bttest
-
-include $(BUILD_EXECUTABLE)
index 4559eee..b953082 100644 (file)
@@ -178,9 +178,9 @@ int bt_enable() {
         goto out;
     }
 
-    LOGI("Starting hcid deamon");
-    if (property_set("ctl.start", "hcid") < 0) {
-        LOGE("Failed to start hcid");
+    LOGI("Starting bluetoothd deamon");
+    if (property_set("ctl.start", "bluetoothd") < 0) {
+        LOGE("Failed to start bluetoothd");
         goto out;
     }
     sleep(HCID_START_DELAY_SEC);
@@ -198,9 +198,9 @@ int bt_disable() {
     int ret = -1;
     int hci_sock = -1;
 
-    LOGI("Stopping hcid deamon");
-    if (property_set("ctl.stop", "hcid") < 0) {
-        LOGE("Error stopping hcid");
+    LOGI("Stopping bluetoothd deamon");
+    if (property_set("ctl.stop", "bluetoothd") < 0) {
+        LOGE("Error stopping bluetoothd");
         goto out;
     }
     usleep(HCID_STOP_DELAY_USEC);
@@ -255,3 +255,17 @@ out:
     if (hci_sock >= 0) close(hci_sock);
     return ret;
 }
+
+int ba2str(const bdaddr_t *ba, char *str) {
+    return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+                ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
+}
+
+int str2ba(const char *str, bdaddr_t *ba) {
+    int i;
+    for (i = 5; i >= 0; i--) {
+        ba->b[i] = (uint8_t) strtoul(str, &str, 16);
+        str++;
+    }
+    return 0;
+}
index 64603e9..70085e4 100644 (file)
@@ -19,6 +19,7 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+#include <bluetooth/bluetooth.h>
 
 /* Enable the bluetooth interface.
  *
@@ -41,6 +42,9 @@ int bt_disable();
 /* Returns 1 if enabled, 0 if disabled, and -ve on error */
 int bt_is_enabled();
 
+int ba2str(const bdaddr_t *ba, char *str);
+int str2ba(const char *str, bdaddr_t *ba);
+
 #ifdef __cplusplus
 }
 #endif
index c0ab284..506d7a0 100644 (file)
@@ -15,6 +15,7 @@
 #ifdef __cplusplus
 #endif
 #include <sys/socket.h>
+#include <sys/ioctl.h>
 #define HCI_MAX_DEV 16
 #define HCI_MAX_ACL_SIZE 1024
 #define HCI_MAX_SCO_SIZE 255
@@ -675,4 +676,21 @@ enum {
 
        HCI_SECMGR
 };
+struct sockaddr_hci {
+       sa_family_t     hci_family;
+       unsigned short  hci_dev;
+};
+struct hci_conn_info {
+        uint16_t handle;
+        bdaddr_t bdaddr;
+        uint8_t  type;
+        uint8_t  out;
+        uint16_t state;
+        uint32_t link_mode;
+};
+struct hci_conn_list_req {
+        uint16_t dev_id;
+        uint16_t conn_num;
+        struct hci_conn_info conn_info[0];
+};
 #endif
diff --git a/bluez-clean-headers/bluetooth/l2cap.h b/bluez-clean-headers/bluetooth/l2cap.h
new file mode 100644 (file)
index 0000000..5fd8162
--- /dev/null
@@ -0,0 +1,85 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __L2CAP_H
+#define __L2CAP_H
+
+#ifdef __cplusplus
+#endif
+#include <sys/socket.h>
+#define L2CAP_DEFAULT_MTU 672
+#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
+#define L2CAP_CONN_TIMEOUT (HZ * 40)
+#define L2CAP_OPTIONS 0x01
+#define L2CAP_CONNINFO 0x02
+#define L2CAP_LM 0x03
+#define L2CAP_LM_MASTER 0x0001
+#define L2CAP_LM_AUTH 0x0002
+#define L2CAP_LM_ENCRYPT 0x0004
+#define L2CAP_LM_TRUSTED 0x0008
+#define L2CAP_LM_RELIABLE 0x0010
+#define L2CAP_LM_SECURE 0x0020
+#define L2CAP_COMMAND_REJ 0x01
+#define L2CAP_CONN_REQ 0x02
+#define L2CAP_CONN_RSP 0x03
+#define L2CAP_CONF_REQ 0x04
+#define L2CAP_CONF_RSP 0x05
+#define L2CAP_DISCONN_REQ 0x06
+#define L2CAP_DISCONN_RSP 0x07
+#define L2CAP_ECHO_REQ 0x08
+#define L2CAP_ECHO_RSP 0x09
+#define L2CAP_INFO_REQ 0x0a
+#define L2CAP_INFO_RSP 0x0b
+#define L2CAP_HDR_SIZE 4
+#define L2CAP_CMD_HDR_SIZE 4
+#define L2CAP_CMD_REJ_SIZE 2
+#define L2CAP_CONN_REQ_SIZE 4
+#define L2CAP_CONN_RSP_SIZE 8
+#define L2CAP_CR_SUCCESS 0x0000
+#define L2CAP_CR_PEND 0x0001
+#define L2CAP_CR_BAD_PSM 0x0002
+#define L2CAP_CR_SEC_BLOCK 0x0003
+#define L2CAP_CR_NO_MEM 0x0004
+#define L2CAP_CS_NO_INFO 0x0000
+#define L2CAP_CS_AUTHEN_PEND 0x0001
+#define L2CAP_CS_AUTHOR_PEND 0x0002
+#define L2CAP_CONF_REQ_SIZE 4
+#define L2CAP_CONF_RSP_SIZE 6
+#define L2CAP_CONF_SUCCESS 0x0000
+#define L2CAP_CONF_UNACCEPT 0x0001
+#define L2CAP_CONF_REJECT 0x0002
+#define L2CAP_CONF_UNKNOWN 0x0003
+#define L2CAP_CONF_OPT_SIZE 2
+#define L2CAP_CONF_MTU 0x01
+#define L2CAP_CONF_FLUSH_TO 0x02
+#define L2CAP_CONF_QOS 0x03
+#define L2CAP_CONF_RFC 0x04
+#define L2CAP_CONF_RFC_MODE 0x04
+#define L2CAP_CONF_MAX_SIZE 22
+#define L2CAP_MODE_BASIC 0x00
+#define L2CAP_MODE_RETRANS 0x01
+#define L2CAP_MODE_FLOWCTL 0x02
+#define L2CAP_DISCONN_REQ_SIZE 4
+#define L2CAP_DISCONN_RSP_SIZE 4
+#define L2CAP_INFO_REQ_SIZE 2
+#define L2CAP_INFO_RSP_SIZE 4
+#define L2CAP_IT_CL_MTU 0x0001
+#define L2CAP_IT_FEAT_MASK 0x0002
+#define L2CAP_IR_SUCCESS 0x0000
+#define L2CAP_IR_NOTSUPP 0x0001
+#ifdef __cplusplus
+#endif
+struct sockaddr_l2 {
+       sa_family_t     l2_family;
+       unsigned short  l2_psm;
+       bdaddr_t        l2_bdaddr;
+};
+#endif
diff --git a/brcm_patchram_plus/Android.mk b/brcm_patchram_plus/Android.mk
new file mode 100644 (file)
index 0000000..3f4e8c7
--- /dev/null
@@ -0,0 +1,19 @@
+ifeq ($(BOARD_HAVE_BLUETOOTH_BCM),true)
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# brcm_patchram_plus.c
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := brcm_patchram_plus.c
+
+LOCAL_MODULE := brcm_patchram_plus
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/brcm_patchram_plus/brcm_patchram_plus.c b/brcm_patchram_plus/brcm_patchram_plus.c
new file mode 100644 (file)
index 0000000..7345acd
--- /dev/null
@@ -0,0 +1,580 @@
+/**
+ * brcm_patchram_plus.c
+ *
+ * Copyright (C) 2009 Broadcom Corporation.
+ * 
+ * This software is licensed under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation (the "GPL"), and may
+ * be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details.
+ * 
+ * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php
+ * or by writing to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+/*****************************************************************************
+**                                                                           
+**  Name:          brcm_patchram_plus.c
+**
+**  Description:   This program downloads a patchram files in the HCD format
+**                 to Broadcom Bluetooth based silicon and combo chips and
+**                                and other utility functions.
+**
+**                 It can be invoked from the command line in the form
+**                                             <-d> to print a debug log
+**                                             <--patchram patchram_file>
+**                                             <--baudrate baud_rate>
+**                                             <--bd_addr bd_address>
+**                                             <--enable_lpm>
+**                                             <--enable_hci>
+**                                             uart_device_name
+**
+**                 For example:
+**
+**                 brcm_patchram_plus -d --patchram  \
+**                                             BCM2045B2_002.002.011.0348.0349.hcd /dev/ttyHS0
+**
+**                 It will return 0 for success and a number greater than 0
+**                 for any errors.
+**
+**                 For Android, this program invoked using a 
+**                 "system(2)" call from the beginning of the bt_enable
+**                 function inside the file 
+**                 system/bluetooth/bluedroid/bluetooth.c.
+**
+**                 If the Android system property "ro.bt.bcm_bdaddr_path" is
+**                 set, then the bd_addr will be read from this path.
+**                 This is overridden by --bd_addr on the command line.
+**  
+******************************************************************************/
+
+// TODO: Integrate BCM support into Bluez hciattach
+
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdlib.h>
+
+#ifdef ANDROID
+#include <termios.h>
+#else
+#include <sys/termios.h>
+#endif
+
+#include <string.h>
+#include <signal.h>
+
+#include <cutils/properties.h>
+
+#ifndef N_HCI
+#define N_HCI  15
+#endif
+
+#define HCIUARTSETPROTO                _IOW('U', 200, int)
+#define HCIUARTGETPROTO                _IOR('U', 201, int)
+#define HCIUARTGETDEVICE       _IOR('U', 202, int)
+
+#define HCI_UART_H4            0
+#define HCI_UART_BCSP  1
+#define HCI_UART_3WIRE 2
+#define HCI_UART_H4DS  3
+#define HCI_UART_LL            4
+
+
+int uart_fd = -1;
+int hcdfile_fd = -1;
+int termios_baudrate = 0;
+int bdaddr_flag = 0;
+int enable_lpm = 0;
+int enable_hci = 0;
+int debug = 0;
+
+struct termios termios;
+unsigned char buffer[1024];
+
+unsigned char hci_reset[] = { 0x01, 0x03, 0x0c, 0x00 };
+
+unsigned char hci_download_minidriver[] = { 0x01, 0x2e, 0xfc, 0x00 };
+
+unsigned char hci_update_baud_rate[] = { 0x01, 0x18, 0xfc, 0x06, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00 };
+
+unsigned char hci_write_bd_addr[] = { 0x01, 0x01, 0xfc, 0x06, 
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+unsigned char hci_write_sleep_mode[] = { 0x01, 0x27, 0xfc, 0x0c, 
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+       0x00, 0x00 };
+
+int
+parse_patchram(char *optarg)
+{
+       char *p;
+
+       if (!(p = strrchr(optarg, '.'))) {
+               fprintf(stderr, "file %s not an HCD file\n", optarg);
+               exit(3);
+       }
+
+       p++;
+
+       if (strcasecmp("hcd", p) != 0) {
+               fprintf(stderr, "file %s not an HCD file\n", optarg);
+               exit(4);
+       }
+
+       if ((hcdfile_fd = open(optarg, O_RDONLY)) == -1) {
+               fprintf(stderr, "file %s could not be opened, error %d\n", optarg, errno);
+               exit(5);
+       }
+
+       return(0);
+}
+
+void 
+BRCM_encode_baud_rate(uint baud_rate, unsigned char *encoded_baud)
+{
+       if(baud_rate == 0 || encoded_baud == NULL) {
+               fprintf(stderr, "Baudrate not supported!");
+               return; 
+       }
+
+       encoded_baud[3] = (unsigned char)(baud_rate >> 24);
+       encoded_baud[2] = (unsigned char)(baud_rate >> 16);
+       encoded_baud[1] = (unsigned char)(baud_rate >> 8);
+       encoded_baud[0] = (unsigned char)(baud_rate & 0xFF);
+}
+
+typedef struct {
+       int baud_rate;
+       int termios_value;
+} tBaudRates;
+
+tBaudRates baud_rates[] = {
+       { 115200, B115200 },
+       { 230400, B230400 },
+       { 460800, B460800 },
+       { 500000, B500000 },
+       { 576000, B576000 },
+       { 921600, B921600 },
+       { 1000000, B1000000 },
+       { 1152000, B1152000 },
+       { 1500000, B1500000 },
+       { 2000000, B2000000 },
+       { 2500000, B2500000 },
+       { 3000000, B3000000 },
+#ifndef __CYGWIN__
+       { 3500000, B3500000 },
+       { 4000000, B4000000 }
+#endif
+};
+
+int
+validate_baudrate(int baud_rate, int *value)
+{
+       unsigned int i;
+
+       for (i = 0; i < (sizeof(baud_rates) / sizeof(tBaudRates)); i++) {
+               if (baud_rates[i].baud_rate == baud_rate) {
+                       *value = baud_rates[i].termios_value;
+                       return(1);
+               }
+       }
+
+       return(0);
+}
+
+int
+parse_baudrate(char *optarg)
+{
+       int baudrate = atoi(optarg);
+
+       if (validate_baudrate(baudrate, &termios_baudrate)) {
+               BRCM_encode_baud_rate(baudrate, &hci_update_baud_rate[6]);
+       }
+
+       return(0);
+}
+
+int
+parse_bdaddr(char *optarg)
+{
+       int bd_addr[6];
+       int i;
+
+       sscanf(optarg, "%02X:%02X:%02X:%02X:%02X:%02X",
+               &bd_addr[5], &bd_addr[4], &bd_addr[3],
+               &bd_addr[2], &bd_addr[1], &bd_addr[0]);
+
+       for (i = 0; i < 6; i++) {
+               hci_write_bd_addr[4 + i] = bd_addr[i];
+       }
+
+       bdaddr_flag = 1;        
+
+       return(0);
+}
+
+int
+parse_enable_lpm(char *optarg)
+{
+       enable_lpm = 1;
+       return(0);
+}
+
+int
+parse_enable_hci(char *optarg)
+{
+       enable_hci = 1;
+       return(0);
+}
+
+int
+parse_cmd_line(int argc, char **argv)
+{
+       int c;
+       int digit_optind = 0;
+
+       typedef int (*PFI)();
+
+       PFI parse_param[] = { parse_patchram, parse_baudrate,
+               parse_bdaddr, parse_enable_lpm, parse_enable_hci };
+
+    while (1)
+    {
+       int this_option_optind = optind ? optind : 1;
+        int option_index = 0;
+
+               static struct option long_options[] = {
+         {"patchram", 1, 0, 0},
+         {"baudrate", 1, 0, 0},
+         {"bd_addr", 1, 0, 0},
+         {"enable_lpm", 0, 0, 0},
+         {"enable_hci", 0, 0, 0},
+         {0, 0, 0, 0}
+               };
+
+               c = getopt_long_only (argc, argv, "d", long_options, &option_index);
+
+               if (c == -1) {
+               break;
+               }
+
+               switch (c) {
+        case 0:
+               printf ("option %s", long_options[option_index].name);
+
+               if (optarg) {
+                       printf (" with arg %s", optarg);
+                       }
+
+               printf ("\n");
+
+                       (*parse_param[option_index])(optarg);
+               break;
+
+               case 'd':
+                       debug = 1;
+               break;
+
+        case '?':
+                       //nobreak
+        default:
+
+                       printf("Usage %s:\n", argv[0]);
+                       printf("\t<-d> to print a debug log\n");
+                       printf("\t<--patchram patchram_file>\n");
+                       printf("\t<--baudrate baud_rate>\n");
+                       printf("\t<--bd_addr bd_address>\n");
+                       printf("\t<--enable_lpm\n");
+                       printf("\t<--enable_hci\n");
+                       printf("\tuart_device_name\n");
+               break;
+
+        }
+       }
+
+       if (optind < argc) {
+               if (optind < argc) {
+                       printf ("%s ", argv[optind]);
+
+                       if ((uart_fd = open(argv[optind], O_RDWR | O_NOCTTY)) == -1) {
+                               fprintf(stderr, "port %s could not be opened, error %d\n", argv[2], errno);
+                       }
+               }
+
+               printf ("\n");
+    }
+
+       return(0);
+}
+
+void
+init_uart()
+{
+       tcflush(uart_fd, TCIOFLUSH);
+       tcgetattr(uart_fd, &termios);
+
+#ifndef __CYGWIN__
+       cfmakeraw(&termios);
+#else
+       termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+                | INLCR | IGNCR | ICRNL | IXON);
+       termios.c_oflag &= ~OPOST;
+       termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+       termios.c_cflag &= ~(CSIZE | PARENB);
+       termios.c_cflag |= CS8;
+#endif
+
+       termios.c_cflag |= CRTSCTS;
+       tcsetattr(uart_fd, TCSANOW, &termios);
+       tcflush(uart_fd, TCIOFLUSH);
+       tcsetattr(uart_fd, TCSANOW, &termios);
+       tcflush(uart_fd, TCIOFLUSH);
+       tcflush(uart_fd, TCIOFLUSH);
+       cfsetospeed(&termios, B115200);
+       cfsetispeed(&termios, B115200);
+       tcsetattr(uart_fd, TCSANOW, &termios);
+}
+
+void
+dump(unsigned char *out, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if (i && !(i % 16)) {
+                       fprintf(stderr, "\n");
+               }
+
+               fprintf(stderr, "%02x ", out[i]);
+       }
+
+       fprintf(stderr, "\n");
+}
+
+void
+read_event(int fd, unsigned char *buffer)
+{
+       int i = 0;
+       int len = 3;
+       int count;
+
+       while ((count = read(fd, &buffer[i], len)) < len) {
+               i += count;
+               len -= count;
+       }
+
+       i += count;
+       len = buffer[2];
+
+       while ((count = read(fd, &buffer[i], len)) < len) {
+               i += count;
+               len -= count;
+       }
+
+       if (debug) {
+               count += i;
+
+               fprintf(stderr, "received %d\n", count);
+               dump(buffer, count);
+       }
+}
+
+void
+hci_send_cmd(unsigned char *buf, int len)
+{
+       if (debug) {
+               fprintf(stderr, "writing\n");
+               dump(buf, len);
+       }
+
+       write(uart_fd, buf, len);
+}
+
+void
+expired(int sig)
+{
+       hci_send_cmd(hci_reset, sizeof(hci_reset));
+       alarm(4);
+}
+
+void
+proc_reset()
+{
+       signal(SIGALRM, expired);
+
+
+       hci_send_cmd(hci_reset, sizeof(hci_reset));
+
+       alarm(4);
+
+       read_event(uart_fd, buffer);
+
+       alarm(0);
+}
+
+void
+proc_patchram()
+{
+       int len;
+
+       hci_send_cmd(hci_download_minidriver, sizeof(hci_download_minidriver));
+
+       read_event(uart_fd, buffer);
+
+       read(uart_fd, &buffer[0], 2);
+
+       while (read(hcdfile_fd, &buffer[1], 3)) {
+               buffer[0] = 0x01;
+
+               len = buffer[3];
+
+               read(hcdfile_fd, &buffer[4], len);
+
+               hci_send_cmd(buffer, len + 4);
+
+               read_event(uart_fd, buffer);
+       }
+
+       proc_reset();
+}
+
+void
+proc_baudrate()
+{
+       hci_send_cmd(hci_update_baud_rate, sizeof(hci_update_baud_rate));
+
+       read_event(uart_fd, buffer);
+
+       cfsetospeed(&termios, termios_baudrate);
+       cfsetispeed(&termios, termios_baudrate);
+       tcsetattr(uart_fd, TCSANOW, &termios);
+
+       if (debug) {
+               fprintf(stderr, "Done setting baudrate\n");
+       }
+}
+
+void
+proc_bdaddr()
+{
+       hci_send_cmd(hci_write_bd_addr, sizeof(hci_write_bd_addr));
+
+       read_event(uart_fd, buffer);
+}
+
+void
+proc_enable_lpm()
+{
+       hci_send_cmd(hci_write_sleep_mode, sizeof(hci_write_sleep_mode));
+
+       read_event(uart_fd, buffer);
+}
+
+void
+proc_enable_hci()
+{
+       int i = N_HCI;
+       int proto = HCI_UART_H4;
+       if (enable_lpm) {
+               proto = HCI_UART_LL;
+       }
+       if (ioctl(uart_fd, TIOCSETD, &i) < 0) {
+               fprintf(stderr, "Can't set line discipline\n");
+               return;
+       }
+
+       if (ioctl(uart_fd, HCIUARTSETPROTO, proto) < 0) {
+               fprintf(stderr, "Can't set hci protocol\n");
+               return;
+       }
+       fprintf(stderr, "Done setting line discpline\n");
+       return;
+}
+
+void
+read_default_bdaddr()
+{
+       int sz;
+       int fd;
+       char path[PROPERTY_VALUE_MAX];
+       char bdaddr[18];
+
+       property_get("ro.bt.bdaddr_path", path, "");
+       if (path[0] == 0)
+               return;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "open(%s) failed: %s (%d)", path, strerror(errno),
+                               errno);
+               return;
+       }
+
+       sz = read(fd, bdaddr, sizeof(bdaddr));
+       if (sz < 0) {
+               fprintf(stderr, "read(%s) failed: %s (%d)", path, strerror(errno),
+                               errno);
+               close(fd);
+               return;
+       } else if (sz != sizeof(bdaddr)) {
+               fprintf(stderr, "read(%s) unexpected size %d", path, sz);
+               close(fd);
+               return;
+       }
+
+       printf("Read default bdaddr of %s\n", bdaddr);
+       parse_bdaddr(bdaddr);
+}
+
+int
+main (int argc, char **argv)
+{
+       read_default_bdaddr();
+
+       parse_cmd_line(argc, argv);
+
+       if (uart_fd < 0) {
+               exit(1);
+       }
+
+       init_uart();
+
+       proc_reset();
+
+       if (hcdfile_fd > 0) {
+               proc_patchram();
+       }
+
+       if (termios_baudrate) {
+               proc_baudrate();
+       }
+
+       if (bdaddr_flag) {
+               proc_bdaddr();
+       }
+
+       if (enable_lpm) {
+               proc_enable_lpm();
+       }
+
+       if (enable_hci) {
+               proc_enable_hci();
+               while (1) {
+                       sleep(UINT_MAX);
+               }
+       }
+
+       exit(0);
+}
index a4dfdab..07501e1 100644 (file)
@@ -21,7 +21,7 @@ dest_dir := $(TARGET_OUT)/etc/bluez
 files := \
        audio.conf \
        input.conf \
-       hcid.conf
+       main.conf
 
 copy_to := $(addprefix $(dest_dir)/,$(files))
 
index 434848a..26ce1c7 100644 (file)
@@ -1,12 +1,37 @@
-# Configuration file for the bluez audio plugin (A2DP)
+# Configuration file for the audio service
 
+# This section contains options which are not specific to any
+# particular interface
+# NOTE: Enable=Sink means that bluetoothd exposes Sink interface for remote
+# devices, and the local device is a Source
 [General]
-Enable=Source,Control,Sink
-Disable=Headset,Gateway
+Enable=Sink,Control
+Disable=Headset,Gateway,Source
 
 # Switch to master role for incoming connections (defaults to true)
 #Master=true
 
+# SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA)
+# Defaults to HCI
+#SCORouting=PCM
+
+# Automatically connect both A2DP and HFP/HSP profiles for incoming
+# connections. Some headsets that support both profiles will only connect the
+# other one automatically so the default setting of true is usually a good
+# idea.
+#AutoConnect=true
+
+# Headset interface specific options (i.e. options which affect how the audio
+# service interacts with remote headset devices)
+#[Headset]
+
+# Set to true to support HFP (in addition to HSP only which is the default)
+# Defaults to false
+#HFP=true
+
+# Maximum number of connected HSP/HFP devices per adapter. Defaults to 1
+#MaxConnections=1
+
 # Just an example of potential config options for the other interfaces
 [A2DP]
 SBCSources=1
diff --git a/data/hcid.conf b/data/hcid.conf
deleted file mode 100644 (file)
index 56df63a..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# HCI daemon configuration file.
-#
-
-# HCId options
-options {
-       # Automatically initialize new devices
-       autoinit yes;
-
-       # Security Manager mode
-       #   none - Security manager disabled
-       #   auto - Use local PIN for incoming connections
-       #   user - Always ask user for a PIN
-       #
-       security user;
-
-       # Pairing mode
-       #   none  - Pairing disabled
-       #   multi - Allow pairing with already paired devices
-       #   once  - Pair once and deny successive attempts
-       pairing multi;
-}
-
-# Default settings for HCI devices
-device {
-       # Local device name
-       #   %d - device id
-       #   %h - host name
-       #   %b - ro.product.brand
-       #   %m - ro.product.model
-       #   %n - ro.product.name
-       name "%m";
-
-       # Local device class
-       #     0x400000  - Service class: Telephony
-       #     0x000200  - Major class: Phone
-       #     0x00000C  - Minor class: Smart phone
-       class 0x40020C;
-
-       # Default packet type
-       #pkt_type DH1,DM1,HV1;
-
-       # Inquiry and Page scan
-       iscan disable;
-       pscan enable;
-
-       # Page timeout (in 0.625ms slots): 10 seconds
-       pageto 16384;
-
-       # Default link mode
-       #   none   - no specific policy
-       #   accept - always accept incoming connections
-       #   master - become master on incoming connections,
-       #            deny role switch on outgoing connections
-       lm accept;
-
-       # Default link policy
-       #   none    - no specific policy
-       #   rswitch - allow role switch
-       #   hold    - allow hold mode
-       #   sniff   - allow sniff mode
-       #   park    - allow park mode
-       lp rswitch,hold,sniff,park;
-}
index 0fe7b0b..abfb64f 100644 (file)
@@ -1,5 +1,9 @@
-[Bluetooth Service]
-Identifier=input
-Name=Input service
-Description=Bluetooth HID based Input service
-Autostart=false
+# Configuration file for the input service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Set idle timeout (in minutes) before the connection will
+# be disconnect (defaults to 0 for no timeout)
+#IdleTimeout=30
diff --git a/data/main.conf b/data/main.conf
new file mode 100644 (file)
index 0000000..8153eb2
--- /dev/null
@@ -0,0 +1,59 @@
+[General]
+
+# List of plugins that should not be loaded on bluetoothd startup
+#DisablePlugins = network,input
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# %b - substituted for ro.product.brand
+# %m - substituted for ro.product.model
+# %n - substituted for ro.product.name
+Name = %m
+
+# Default device class. Only the major and minor device class bits are
+# considered.
+# Local device class
+#     0x400000  - Service class: Telephony
+#     0x000200  - Major class: Phone
+#     0x00000C  - Minor class: Smart phone
+Class = 0x40020C
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+DiscoverableTimeout = 120
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+PairableTimeout = 0
+
+# Use some other page timeout than the controller default one
+# which is 16384 (10 seconds).
+PageTimeout = 8192
+
+# Discover scheduler interval used in Adapter.DiscoverDevices
+# The value is in seconds. Defaults is 0 to use controller scheduler.
+DiscoverSchedulerInterval = 0
+
+# What value should be assumed for the adapter Powered property when
+# SetProperty(Powered, ...) hasn't been called yet. Defaults to true
+InitiallyPowered = true
+
+# Remember the previously stored Powered state when initializing adapters
+RememberPowered = true
+
+# Use vendor, product and version information for DID profile support.
+# The values are separated by ":" and VID, PID and version.
+DeviceID = android:generic:1.5
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to true.
+ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+NameResolving = true
diff --git a/tools/Android.mk b/tools/Android.mk
new file mode 100644 (file)
index 0000000..87dedad
--- /dev/null
@@ -0,0 +1,150 @@
+BUILD_EXTRA_BT_TOOLS:=false
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# bttest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bttest.c
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/../bluedroid/include \
+       system/bluetooth/bluez-clean-headers
+
+LOCAL_SHARED_LIBRARIES := libbluedroid
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := bttest
+
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(BUILD_EXTRA_BT_TOOLS),true)
+
+#
+# socktest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := socktest.c
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/../bluedroid/include \
+       $(LOCAL_PATH)/../bluez-clean-headers
+
+LOCAL_SHARED_LIBRARIES := libbluedroid
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := socktest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# asocket_test
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := asocket_test.c
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/../bluez-clean-headers
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := asocket_test
+
+include $(BUILD_EXECUTABLE)
+
+#
+# sock_shutdown_test
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := sock_shutdown_test.c
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/../bluez-clean-headers
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := sock_shutdown_test
+
+include $(BUILD_EXECUTABLE)
+
+#
+# sock_shutdown_bug_l2cap
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := sock_shutdown_bug_l2cap.c
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/../bluez-clean-headers
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := sock_shutdown_bug_l2cap
+
+LOCAL_SHARED_LIBRARIES := libbluetooth
+
+include $(BUILD_EXECUTABLE)
+
+#
+# sock_shutdown_bug_rfcomm
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := sock_shutdown_bug_rfcomm.c
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/../bluez-clean-headers
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := sock_shutdown_bug_rfcomm
+
+LOCAL_SHARED_LIBRARIES := libbluetooth
+
+include $(BUILD_EXECUTABLE)
+
+#
+# sock_shutdown_bug_tcp
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := sock_shutdown_bug_tcp.c
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := sock_shutdown_bug_tcp
+
+LOCAL_SHARED_LIBRARIES := libbluetooth
+
+include $(BUILD_EXECUTABLE)
+#
+# pipetest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := pipetest.c
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := pipetest
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/tools/asocket_test.c b/tools/asocket_test.c
new file mode 100644 (file)
index 0000000..0c00526
--- /dev/null
@@ -0,0 +1,742 @@
+/*
+** Copyright 2009 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/** socket testing  */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/l2cap.h>
+
+#include "cutils/abort_socket.h"
+
+enum sock_type {
+    UNIX = 0,
+    RFCOMM,
+    SCO,
+    L2CAP,
+    TCP,
+};
+
+struct thread_args {
+    int fd;
+    int type;
+    int delay;
+};
+
+struct sockaddr_un  local_addr_un  = {AF_UNIX, "/data/foo"};
+struct sockaddr_rc  local_addr_rc  = {AF_BLUETOOTH, *BDADDR_ANY, 4};
+struct sockaddr_sco local_addr_sco = {AF_BLUETOOTH, *BDADDR_LOCAL};
+struct sockaddr_l2  local_addr_l2  = {AF_BLUETOOTH, htobs(0x1001), *BDADDR_ANY, 0};
+struct sockaddr_in  local_addr_in  = {AF_INET, 9999, {0}, {0}};
+
+struct sockaddr_un  remote_addr_un  ;
+struct sockaddr_rc  remote_addr_rc  ;
+struct sockaddr_sco remote_addr_sco ;
+struct sockaddr_l2  remote_addr_l2  ;
+struct sockaddr_in  remote_addr_in  ;
+
+static void print_events(int events) {
+    if (events & POLLIN) printf("POLLIN ");
+    if (events & POLLPRI) printf("POLLPRI ");
+    if (events & POLLOUT) printf("POLLOUT ");
+    if (events & POLLERR) printf("POLLERR ");
+    if (events & POLLHUP) printf("POLLHUP ");
+    if (events & POLLNVAL) printf("POLLNVAL ");
+    printf("\n");
+}
+
+static void print_fds(struct pollfd *ufds, nfds_t nfds) {
+    unsigned int i;
+    for (i=0; i<nfds; i++)
+        printf("%d ", ufds[i].fd);
+}
+
+static int _socket(int type) {
+    int ret;
+    int family = -1;
+    int typ = -1;
+    int protocol = -1;
+
+    switch (type) {
+    case UNIX:
+        family = PF_UNIX;
+        typ = SOCK_STREAM;
+        protocol = 0;
+        break;
+    case RFCOMM:
+        family = PF_BLUETOOTH;
+        typ = SOCK_STREAM;
+        protocol = BTPROTO_RFCOMM;
+        break;
+    case SCO:
+        family = PF_BLUETOOTH;
+        typ = SOCK_SEQPACKET;
+        protocol = BTPROTO_SCO;
+        break;
+    case L2CAP:
+        family = PF_BLUETOOTH;
+        typ = SOCK_SEQPACKET;
+        protocol = BTPROTO_L2CAP;
+        break;
+    case TCP:
+        family = PF_INET;
+        typ = SOCK_STREAM;
+        protocol = 0;
+        break;
+    }
+
+    printf("%d: socket()\n", gettid());
+    ret = socket(family, typ, protocol);
+    printf("%d: socket() = %d\n", gettid(), ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _close(int fd, int type) {
+    int ret;
+
+    printf("%d: close(%d)\n", gettid(), fd);
+    ret = close(fd);
+    printf("%d: close(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _bind(int fd, int type) {
+    int len = 0;
+    int ret;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        unlink(local_addr_un.sun_path);
+        addr = (struct sockaddr *) &local_addr_un;
+        len = sizeof(local_addr_un);
+        break;
+    case RFCOMM:
+        addr = (struct sockaddr *) &local_addr_rc;
+        len = sizeof(local_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &local_addr_sco;
+        len = sizeof(local_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &local_addr_l2;
+        len = sizeof(local_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &local_addr_in;
+        len = sizeof(local_addr_in);
+        break;
+    }
+
+    printf("%d: bind(%d)\n", gettid(), fd);
+    ret = bind(fd, addr, len);
+    printf("%d: bind(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _listen(int fd, int type) {
+    int ret;
+
+    printf("%d: listen(%d)\n", gettid(), fd);
+    ret = listen(fd, 1);
+    printf("%d: listen(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _read(int fd) {
+    int ret;
+    char buf;
+
+    printf("%d: read(%d)\n", gettid(), fd);
+    ret = read(fd, &buf, 1);
+    printf("%d: read(%d) = %d [%d]\n", gettid(), fd, ret, (int)buf);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+
+static int _accept(int fd, int type) {
+    int ret;
+    int len;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        addr = (struct sockaddr *) &remote_addr_un;
+        len = sizeof(remote_addr_un);
+        break;
+    case RFCOMM:
+        addr = (struct sockaddr *) &remote_addr_rc;
+        len = sizeof(remote_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &remote_addr_sco;
+        len = sizeof(remote_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &remote_addr_l2;
+        len = sizeof(remote_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &remote_addr_in;
+        len = sizeof(remote_addr_in);
+        break;
+    }
+
+    printf("%d: accept(%d)\n", gettid(), fd);
+    ret = accept(fd, addr, &len);
+    printf("%d: accept(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+    else {
+        printf("\tlen = %d\n", len);
+    }
+
+    return ret;
+}
+
+static int _connect(int fd, int type) {
+    int ret;
+    int len = 0;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        addr = (struct sockaddr *) &local_addr_un;
+        len = sizeof(local_addr_un);
+        break;
+    case RFCOMM:
+        addr = (struct sockaddr *) &local_addr_rc;
+        len = sizeof(local_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &local_addr_sco;
+        len = sizeof(local_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &local_addr_l2;
+        len = sizeof(local_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &local_addr_in;
+        len = sizeof(local_addr_in);
+        break;
+    }
+
+    printf("%d: connect(%d)\n", gettid(), fd);
+    ret = connect(fd, addr, len);
+    printf("%d: connect(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _write(int fd, int type) {
+    int ret;
+    char buf = 69;
+
+    printf("%d: write(%d)\n", gettid(), fd);
+    ret = write(fd, &buf, 1);
+    printf("%d: write(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _shutdown(int fd, int how) {
+    int ret;
+
+    printf("%d: shutdown(%d)\n", gettid(), fd);
+    ret = shutdown(fd, how);
+    printf("%d: shutdown(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _poll(struct pollfd *ufds, nfds_t nfds, int timeout) {
+    int ret;
+    unsigned int i;
+
+    printf("%d: poll(", gettid());
+    print_fds(ufds, nfds);
+    printf(")\n");
+    ret = poll(ufds, nfds, timeout);
+    printf("%d: poll() = %d\n", gettid(), ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+    if (ret > 0) {
+        for (i=0; i<nfds; i++) {
+            if (ufds[i].revents) {
+                printf("\tfd %d ", ufds[i].fd); print_events(ufds[i].revents);
+            }
+        }
+    }
+    return ret;
+}
+
+static void thread_delay_close(struct thread_args *args) {
+    printf("%d: START\n", gettid());
+    sleep(args->delay);
+    _close(args->fd, args->type);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_poll(void *args) {
+    int fd = (int)args;
+    struct pollfd pfd;
+    printf("%d: START\n", gettid());
+    pfd.fd = fd;
+    pfd.events = 0;
+    _poll(&pfd, 1, -1);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_read(void *args) {
+    int fd = (int)args;
+    printf("%d: START\n", gettid());
+    _read(fd);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_pollin(void *args) {
+    int fd = (int)args;
+    struct pollfd pfd;
+    printf("%d: START\n", gettid());
+    pfd.fd = fd;
+    pfd.events = POLLIN;
+    _poll(&pfd, 1, -1);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_shutdown(int fd) {
+    printf("%d: START\n", gettid());
+    sleep(4);
+    _shutdown(fd, SHUT_RDWR);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_accept(struct thread_args *args) {
+    printf("%d: START\n", gettid());
+    sleep(args->delay);
+    _accept(args->fd, args->type);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_connect(struct thread_args *args) {
+    printf("%d: START\n", gettid());
+    sleep(args->delay);
+    _connect(args->fd, args->type);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_delay_close_write(struct thread_args *args) {
+    printf("%d: START\n", gettid());
+    sleep(args->delay);
+    _close(args->fd, args->type);
+    sleep(args->delay);
+    _write(args->fd, args->type);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_accept_write(struct thread_args *args) {
+    printf("%d: START\n", gettid());
+    sleep(args->delay);
+    _accept(args->fd, args->type);
+    sleep(args->delay);
+    _write(args->fd, args->type);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_delay_connect(struct thread_args *args) {
+    printf("%d: START\n", gettid());
+    sleep(args->delay);
+    args->fd = _socket(args->type);
+    _connect(args->fd, args->type);
+    printf("%d: END\n", gettid());
+}
+
+static int do_accept_accept_accept(int type) {
+    int fd;
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    while (1) {
+        _accept(fd, type);
+    }
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_accept_and_close(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 1};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_delay_close, (void *)&args);
+
+    _accept(fd, type);
+
+    pthread_join(thread, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_accept_shutdown(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 0};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_accept, (void *)&args);
+
+    sleep(4);
+    _shutdown(fd, SHUT_RDWR);
+
+    pthread_join(thread, NULL);
+
+    _close(fd, type);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_connect_shutdown(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 0};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_connect, (void *)&args);
+
+    sleep(4);
+    _shutdown(fd, SHUT_RDWR);
+
+    pthread_join(thread, NULL);
+
+    _close(fd, type);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+// accept in one thread. close then write in another
+static int do_accept_close_write(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 1};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_delay_close_write, (void *)&args);
+
+    _accept(fd, type);
+
+    pthread_join(thread, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_poll_poll_poll_shutdown(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_poll, (void *)fd);
+
+    sleep(1);
+
+    _shutdown(fd, SHUT_RDWR);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    _close(fd, type);
+
+    return 0;
+}
+
+static int do_poll_poll_poll_close(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_poll, (void *)fd);
+
+    sleep(1);
+
+    _close(fd, type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    return 0;
+}
+
+static int do_read_read_read_close(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_read, (void *)fd);
+
+    sleep(1);
+
+    _close(fd, type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    return 0;
+}
+
+static int do_read_read_read_shutdown(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_read, (void *)fd);
+
+    sleep(1);
+
+    _shutdown(fd, SHUT_RDWR);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    _close(fd, type);
+
+    return 0;
+}
+
+static int do_connected_read1_shutdown1(int type) {
+    int fd1, fd2;
+    pthread_t t1;
+    pthread_t t2;
+    struct thread_args a1 = {-1, type, 0};
+    struct thread_args a2 = {-1, type, 2};
+
+    fd1 = _socket(type);
+    if (fd1 < 0) goto error;
+
+    if (_bind(fd1, type) < 0) goto error;
+
+    if (_listen(fd1, type) < 0) goto error;
+
+    a1.fd = fd1;
+    pthread_create(&t1, NULL, (void *)thread_accept_write, (void *)&a1);
+
+    fd2 = _socket(type);
+    if (_connect(fd2, type)) goto error;
+
+    pthread_create(&t2, NULL, (void *)thread_shutdown, (void *)&fd2);
+    
+    while (1) if (_read(fd2)) break;
+
+    pthread_join(t1, NULL);
+    pthread_join(t2, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+// accept in one thread, connect from two different threads
+static int do_accept_connect_connect(int type) {
+    int fd;
+    pthread_t t1;
+    pthread_t t2;
+    struct thread_args a1 = {-1, type, 1};
+    struct thread_args a2 = {-1, type, 2};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    pthread_create(&t1, NULL, (void *)thread_delay_connect, (void *)&a1);
+    pthread_create(&t2, NULL, (void *)thread_delay_connect, (void *)&a2);
+
+    _accept(fd, type);
+
+    pthread_join(t1, NULL);
+    pthread_join(t2, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+struct {
+    char *name;
+    int (*ptr)(int);
+} action_table[]  = {
+    {"accept_accept_accept", do_accept_accept_accept},
+    {"accept_and_close", do_accept_and_close},
+    {"accept_shutdown", do_accept_shutdown},
+    {"connect_shutdown", do_connect_shutdown},
+    {"accept_close_write", do_accept_close_write},
+    {"accept_connect_connect", do_accept_connect_connect},
+    {"poll_poll_poll_shutdown", do_poll_poll_poll_shutdown},
+    {"poll_poll_poll_close", do_poll_poll_poll_close},
+    {"read_read_read_shutdown", do_read_read_read_shutdown},
+    {"read_read_read_close", do_read_read_read_close},
+    {"connected_read1_shutdown1", do_connected_read1_shutdown1},
+    {NULL, NULL},
+};
+
+struct {
+    char *name;
+    enum sock_type type;
+} type_table[]  = {
+    {"unix", UNIX},
+    {"rfcomm", RFCOMM},
+    {"sco", SCO},
+    {"l2cap", L2CAP},
+    {"tcp", TCP},
+    {NULL, -1},
+};
+
+static void usage() {
+    int i;
+
+    printf("socktest TYPE ACTION\n");
+    printf("\nTYPE:\n");
+    for (i = 0; type_table[i].name; i++) {
+        printf("\t%s\n", type_table[i].name);
+    }
+    printf("\nACTION:\n");
+    for (i = 0; action_table[i].name; i++) {
+        printf("\t%s\n", action_table[i].name);
+    }
+}
+
+int main(int argc, char **argv) {
+    int i;
+    int type = -1;
+
+    if (argc != 3) {
+        usage();
+        return -1;
+    }
+    for (i = 0; type_table[i].name; i++) {
+        if (!strcmp(argv[1], type_table[i].name)) {
+            type = type_table[i].type;
+            break;
+        }
+    }
+    if (type == -1) {
+        usage();
+        return -1;
+    }
+    for (i = 0; action_table[i].name; i++) {
+        if (!strcmp(argv[2], action_table[i].name)) {
+            printf("TYPE = %s ACTION = %s\n", type_table[type].name,
+                    action_table[i].name);
+            return (*action_table[i].ptr)(type);
+        }
+    }
+    usage();
+    return -1;
+}
similarity index 100%
rename from bluedroid/bttest.c
rename to tools/bttest.c
diff --git a/tools/pipetest.c b/tools/pipetest.c
new file mode 100644 (file)
index 0000000..634e4b9
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Helper to test linux pipe's */
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+static void print_events(int events) {
+    if (events & POLLIN) printf("POLLIN ");
+    if (events & POLLPRI) printf("POLLPRI ");
+    if (events & POLLOUT) printf("POLLOUT ");
+    if (events & POLLERR) printf("POLLERR ");
+    if (events & POLLHUP) printf("POLLHUP ");
+    if (events & POLLNVAL) printf("POLLNVAL ");
+    printf("\n");
+}
+
+static int _socketpair(int fd[2]) {
+    int ret;
+    printf("%d: socketpair()\n", gettid());
+    ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+    printf("%d: socketpair() = %d\n", gettid(), ret);
+    if (ret) printf("\terr %d (%s)\n", errno, strerror(errno));
+    return ret;
+}   
+
+static int _close(int fd) {
+    int ret;
+    printf("%d: close(%d)\n", gettid(), fd);
+    ret = close(fd);
+    printf("%d: close(%d) = %d\n", gettid(), fd, ret);
+    if (ret) printf("\terr %d (%s)\n", errno, strerror(errno));
+    return ret;
+}
+
+static int _poll(struct pollfd *ufds, nfds_t nfds, int timeout) {
+    int ret;
+    unsigned int i;
+    printf("%d: poll()\n", gettid());
+    ret = poll(ufds, nfds, timeout);
+    printf("%d: poll() = %d\n", gettid(), ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+    if (ret > 0) {
+        for (i=0; i<nfds; i++) {
+            if (ufds[i].revents) {
+                printf("\tfd %d ", ufds[i].fd); print_events(ufds[i].revents);
+            }
+        }
+    }
+    return ret;
+}
+
+static int _write(int fd, char *buf, int len) {
+    int ret;
+
+    printf("%d: write(%d)\n", gettid(), fd);
+    ret = write(fd, buf, len);
+    printf("%d: write(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _read(int fd) {
+    int ret;
+    char buf;
+
+    printf("%d: read(%d)\n", gettid(), fd);
+    ret = read(fd, &buf, 1);
+    printf("%d: read(%d) = %d [%d]\n", gettid(), fd, ret, (int)buf);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _shutdown(int fd, int how) {
+    int ret;
+
+    printf("%d: shutdown(%d)\n", gettid(), fd);
+    ret = shutdown(fd, how);
+    printf("%d: shutdown(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+static void thread_poll(void *args) {
+    int fd = (int)args;
+    struct pollfd pfd;
+    printf("%d: START\n", gettid());
+    pfd.fd = fd;
+    pfd.events = 0;
+    _poll(&pfd, 1, -1);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_pollin(void *args) {
+    int fd = (int)args;
+    struct pollfd pfd;
+    printf("%d: START\n", gettid());
+    pfd.fd = fd;
+    pfd.events = POLLIN;
+    _poll(&pfd, 1, -1);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_pollin_rand_delay(void *args) {
+    int fd = (int)args;
+    struct pollfd pfd;
+    int delay = (int)((double)random() * (10000000.0 / 2147483647.0));
+    printf("%d: START (delay = %d)\n", gettid(), delay);
+    pfd.fd = fd;
+    pfd.events = POLLIN;
+    usleep(delay);
+    _poll(&pfd, 1, -1);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_read(void *args) {
+    int fd = (int)args;
+    printf("%d: START\n", gettid());
+    _read(fd);
+    printf("%d: END\n", gettid());
+}
+
+static void thread_close(void *args) {
+    int fd = (int)args;
+    printf("%d: START\n", gettid());
+    _close(fd);
+    printf("%d: END\n", gettid());
+}
+
+static int do_poll_poll_close() {
+    pthread_t t1;
+    pthread_t t2;
+    int fd[2];
+
+    if (pipe(fd)) return -1;
+
+    pthread_create(&t1, NULL, (void *)thread_poll, NULL);
+    pthread_create(&t2, NULL, (void *)thread_poll, NULL);
+
+    sleep(1);
+
+    _close(fd[1]);
+
+    pthread_join(t1, NULL);
+    pthread_join(t2, NULL);
+
+    return 0;
+}
+
+static int do_socketpair_poll1_shutdown2() {
+    int fd[2];
+    pthread_t t;
+
+    if (_socketpair(fd)) return -1;
+
+    pthread_create(&t, NULL, (void *)thread_poll, (void *)fd[1]);
+
+    sleep(1);
+
+    _shutdown(fd[0], SHUT_RDWR);    
+
+    sleep(1);
+
+    _close(fd[0]);
+
+    pthread_join(t, NULL);
+
+    return 0;
+}
+
+static int do_socketpair_poll1_shutdown1() {
+    int fd[2];
+    pthread_t t;
+
+    if (_socketpair(fd)) return -1;
+
+    pthread_create(&t, NULL, (void *)thread_poll, (void *)fd[0]);
+
+    sleep(1);
+
+    _shutdown(fd[0], SHUT_RDWR);    
+
+    sleep(1);
+
+    _close(fd[0]);
+
+    pthread_join(t, NULL);
+
+    return 0;
+}
+
+static int do_socketpair_poll1_close1() {
+    int fd[2];
+    pthread_t t;
+
+    if (_socketpair(fd)) return -1;
+
+    pthread_create(&t, NULL, (void *)thread_poll, (void *)fd[0]);
+
+    sleep(1);
+
+    _close(fd[0]);
+
+    pthread_join(t, NULL);
+
+    return 0;
+}
+
+static int do_socketpair_read1_shutdown1() {
+    int fd[2];
+    pthread_t t;
+
+    if (_socketpair(fd)) return -1;
+
+    pthread_create(&t, NULL, (void *)thread_read, (void *)fd[0]);
+
+    sleep(1);
+
+    _shutdown(fd[0], SHUT_RDWR);    
+
+    sleep(1);
+
+    _close(fd[0]);
+
+    pthread_join(t, NULL);
+
+    return 0;
+}
+
+static int do_pipe_pipe_pipe() {
+    int fd[2];
+    int i;
+
+    while (1) {
+        if (pipe(fd)) {
+            printf("pipe: %s\n", strerror(errno));
+            return -1;
+        }
+        printf("%d %d\n", fd[0], fd[1]);
+        close(fd[0]);
+        close(fd[1]);
+    }
+
+    return 0;
+}
+static int do_pollin_pollin_write() {
+    pthread_t t1;
+    pthread_t t2;
+    int fd[2];
+    char buf = 'a';
+    int i;
+
+    if (pipe(fd)) return -1;
+
+    pthread_create(&t1, NULL, (void *)thread_pollin, (void *)fd[0]);
+    pthread_create(&t2, NULL, (void *)thread_pollin, (void *)fd[0]);
+
+    sleep(1);
+
+    for (i = 0; i < 100; i++)
+        _write(fd[1], &buf, 1);
+
+    pthread_join(t1, NULL);
+    pthread_join(t2, NULL);
+
+    return 0;
+}
+
+static int do_pollin_pollin_pollin_write_pollin_pollin_pollin() {
+    const int MAX_T = 10;
+    pthread_t t[MAX_T];
+    int fd[2];
+    char buf = 'a';
+    int i;
+
+    if (pipe(fd)) return -1;
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_pollin_rand_delay, (void *)fd[0]);
+
+    sleep(5);
+
+    _write(fd[1], &buf, 1);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    _close(fd[0]);
+    _close(fd[1]);
+
+    return 0;
+}
+
+static int do_poll_poll_shutdown() {
+#if 0
+    pthread_t t1;
+    pthread_t t2;
+    int fd[2];
+
+    if (pipe(fd)) return -1;
+
+    pthread_create(&t1, NULL, (void *)thread_poll, (void *)fd[0]);
+    pthread_create(&t2, NULL, (void *)thread_poll, (void *)fd[0]);
+
+    sleep(1);
+
+    _shutdown(fd[1], SHUT_RDWR);
+
+    pthread_join(t1, NULL);
+    pthread_join(t2, NULL);
+#endif
+
+    return -1;
+}
+
+static int THREADS = 100;
+
+static int do_close_poll_poll_poll() {
+    pthread_t t[THREADS];
+    int i;
+    int fd[2];
+
+    if (pipe(fd)) return -1;
+
+    _close(fd[1]);
+
+    for (i = 0; i < THREADS; i++)
+        pthread_create(&t[i], NULL, (void *)thread_poll, (void *)fd[0]);
+
+    for (i = 0; i < THREADS; i++)
+        pthread_join(t[i], NULL);
+
+    return 0;
+}
+
+static int do_close_close_close() {
+    pthread_t t[THREADS];
+    int i;
+    int fd[2];
+
+    if (pipe(fd)) return -1;
+
+    for (i = 0; i < THREADS; i++)
+        pthread_create(&t[i], NULL, (void *)thread_close, (void *)fd[i%2]);
+
+    return 0;
+}
+
+static int pipe_close_w_close_r_repeat() {
+    int fd[2];
+    pthread_t t;
+    int i;
+
+    for (i = 0; i < THREADS; i++) {
+        if (pipe(fd)) return -1;
+        pthread_create(&t, NULL, (void *)thread_poll, (void *)fd[0]);
+        _close(fd[1]);
+        _close(fd[0]);
+        pthread_join(t, NULL);
+    }
+
+    return 0;
+}
+
+struct {
+    char *name;
+    int (*ptr)();
+} function_table[]  = {
+    {"socketpair_poll1_shutdown2", do_socketpair_poll1_shutdown2},
+    {"socketpair_poll1_shutdown1", do_socketpair_poll1_shutdown1},
+    {"socketpair_poll1_close1", do_socketpair_poll1_close1},
+    {"socketpair_read1_shutdown1", do_socketpair_read1_shutdown1},
+    {"pipe_pipe_pipe", do_pipe_pipe_pipe},
+    {"poll_poll_close", do_poll_poll_close},
+    {"pollin_pollin_write", do_pollin_pollin_write},
+    {"pollin_pollin_pollin_write_pollin_pollin_pollin", do_pollin_pollin_pollin_write_pollin_pollin_pollin},
+    {"poll_poll_shutdown", do_poll_poll_shutdown},
+    {"close_poll_poll_poll", do_close_poll_poll_poll},
+    {"close_close_close", do_close_close_close},
+    {"pipe_close_w_close_w_repeat", pipe_close_w_close_r_repeat},
+    {NULL, NULL},
+};
+
+static void usage() {
+    int i;
+
+    printf("Usage:\n");
+    for (i = 0; function_table[i].name; i++) {
+        printf("\tpipetest %s\n", function_table[i].name);
+    }
+}
+
+int main(int argc, char **argv) {
+    int i;
+
+    if (argc != 2) {
+        usage();
+        return -1;
+    }
+    for (i = 0; function_table[i].name; i++) {
+        if (!strcmp(argv[1], function_table[i].name)) {
+            printf("%s\n", function_table[i].name);
+            return (*function_table[i].ptr)();
+        }
+    }
+    usage();
+    return -1;
+}
diff --git a/tools/sock_shutdown_bug_l2cap.c b/tools/sock_shutdown_bug_l2cap.c
new file mode 100644 (file)
index 0000000..1bb2373
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+** Copyright 2009 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+
+int main(int argc, char **argv) {
+   int fd;
+   int ret;
+   long flags;
+   struct sockaddr_l2 addr;
+
+   addr.l2_family = AF_BLUETOOTH;
+   str2ba("00:01:02:0A:0B:0C", &addr.l2_bdaddr);
+   addr.l2_psm = htobs(1);
+
+   fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+   flags = fcntl(fd, F_GETFL);
+   fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+   connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+
+   sleep(1);
+   shutdown(fd, SHUT_RDWR);
+   sleep(1);
+   close(fd);
+   return 0;
+}
diff --git a/tools/sock_shutdown_bug_rfcomm.c b/tools/sock_shutdown_bug_rfcomm.c
new file mode 100644 (file)
index 0000000..7c4a37f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+** Copyright 2009 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+int main(int argc, char **argv) {
+    int fd;
+    int ret;
+    long flags;
+    struct sockaddr_rc addr;
+
+    addr.rc_family = AF_BLUETOOTH;
+    addr.rc_channel = 19;
+    str2ba("00:17:E8:2C:A8:00", &addr.rc_bdaddr);
+
+    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+    flags = fcntl(fd, F_GETFL);
+    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+    ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+    if (ret < 0) {
+        printf("%d errno %d %s\n", __LINE__, errno, strerror(errno));
+    }
+
+    sleep(2);
+    shutdown(fd, SHUT_RDWR);
+
+
+    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+
+    ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+    if (ret < 0) {
+        printf("%d errno %d %s\n", __LINE__, errno, strerror(errno));
+    }
+    
+    sleep(2);
+
+    shutdown(fd, SHUT_RDWR);
+
+    sleep(2);
+    return 0;
+}
diff --git a/tools/sock_shutdown_bug_tcp.c b/tools/sock_shutdown_bug_tcp.c
new file mode 100644 (file)
index 0000000..6333320
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+** Copyright 2009 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+int main(int argc, char **argv) {
+    int fd;
+    int ret;
+    long flags;
+    struct sockaddr_in addr;
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = 12348;
+
+    fd = socket(PF_INET, SOCK_STREAM, 0);
+
+    ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+    if (ret < 0) {
+        printf("%d errno %d %s\n", __LINE__, errno, strerror(errno));
+    }
+    
+    ret = listen(fd, 1);
+    if (ret < 0) {
+        printf("%d errno %d %s\n", __LINE__, errno, strerror(errno));
+    }
+
+    sleep(2);
+
+    close(fd);
+
+    sleep(2);
+
+    fd = socket(PF_INET, SOCK_STREAM, 0);
+
+    ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+    if (ret < 0) {
+        printf("%d errno %d %s\n", __LINE__, errno, strerror(errno));
+    }
+    
+    sleep(2000000000);
+
+    return 0;
+}
+
diff --git a/tools/sock_shutdown_test.c b/tools/sock_shutdown_test.c
new file mode 100644 (file)
index 0000000..29f6b59
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+** Copyright 2009 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/** testing behavior of shutdown() */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/l2cap.h>
+
+enum sock_type {
+    UNIX = 0,
+    RFCOMM,
+    SCO,
+    L2CAP,
+    TCP,
+};
+
+struct thread_args {
+    int fd;
+    int type;
+    int delay;
+};
+
+struct sockaddr_un  local_addr_un  = {AF_UNIX, "/tmp/foo"};
+struct sockaddr_rc  local_addr_rc  = {AF_BLUETOOTH, *BDADDR_ANY, 4};
+struct sockaddr_sco local_addr_sco = {AF_BLUETOOTH, *BDADDR_LOCAL};
+struct sockaddr_l2  local_addr_l2  = {AF_BLUETOOTH, htobs(0x1001), *BDADDR_ANY, 0};
+struct sockaddr_in  local_addr_in  = {AF_INET, 9999, {0}, {0}};
+
+struct sockaddr_un  remote_addr_un  ;
+struct sockaddr_rc  remote_addr_rc  ;
+struct sockaddr_sco remote_addr_sco ;
+struct sockaddr_l2  remote_addr_l2  ;
+struct sockaddr_in  remote_addr_in  ;
+
+static int _socket(int type) {
+    int ret;
+    int family = -1;
+    int typ = -1;
+    int protocol = -1;
+
+    switch (type) {
+    case UNIX:
+        family = PF_UNIX;
+        typ = SOCK_STREAM;
+        protocol = 0;
+        break;
+    case RFCOMM:
+        family = PF_BLUETOOTH;
+        typ = SOCK_STREAM;
+        protocol = BTPROTO_RFCOMM;
+        break;
+    case SCO:
+        family = PF_BLUETOOTH;
+        typ = SOCK_SEQPACKET;
+        protocol = BTPROTO_SCO;
+        break;
+    case L2CAP:
+        family = PF_BLUETOOTH;
+        typ = SOCK_SEQPACKET;
+        protocol = BTPROTO_L2CAP;
+        break;
+    case TCP:
+        family = PF_INET;
+        typ = SOCK_STREAM;
+        protocol = 0;
+        break;
+    }
+
+    printf("%d: socket()\n", gettid());
+    ret = socket(family, typ, protocol);
+    printf("%d: socket() = %d\n", gettid(), ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _close(int fd) {
+    int ret;
+
+    printf("%d: close(%d)\n", gettid(), fd);
+    ret = close(fd);
+    printf("%d: close(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _bind(int fd, int type) {
+    int len = 0;
+    int ret;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        unlink(local_addr_un.sun_path);
+        addr = (struct sockaddr *) &local_addr_un;
+        len = sizeof(local_addr_un);
+        break;
+    case RFCOMM:
+        addr = (struct sockaddr *) &local_addr_rc;
+        len = sizeof(local_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &local_addr_sco;
+        len = sizeof(local_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &local_addr_l2;
+        len = sizeof(local_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &local_addr_in;
+        len = sizeof(local_addr_in);
+        break;
+    }
+
+    printf("%d: bind(%d)\n", gettid(), fd);
+    ret = bind(fd, addr, len);
+    printf("%d: bind(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _listen(int fd, int type) {
+    int ret;
+
+    printf("%d: listen(%d)\n", gettid(), fd);
+    ret = listen(fd, 1);
+    printf("%d: listen(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _accept(int fd, int type) {
+    int ret;
+    int len;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        addr = (struct sockaddr *) &remote_addr_un;
+        len = sizeof(remote_addr_un);
+        break;
+    case RFCOMM:
+        addr = (struct sockaddr *) &remote_addr_rc;
+        len = sizeof(remote_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &remote_addr_sco;
+        len = sizeof(remote_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &remote_addr_l2;
+        len = sizeof(remote_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &remote_addr_in;
+        len = sizeof(remote_addr_in);
+        break;
+    }
+
+    printf("%d: accept(%d)\n", gettid(), fd);
+    ret = accept(fd, addr, &len);
+    printf("%d: accept(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+    else {
+        printf("\tlen = %d\n", len);
+    }
+
+    return ret;
+}
+
+static int _shutdown(int fd, int how) {
+    int ret;
+
+    printf("%d: shutdown(%d)\n", gettid(), fd);
+    ret = shutdown(fd, how);
+    printf("%d: shutdown(%d) = %d\n", gettid(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static void thread_accept(struct thread_args *args) {
+    printf("%d: START\n", gettid());
+    sleep(args->delay);
+    _accept(args->fd, args->type);
+    printf("%d: END\n", gettid());
+}
+
+static int do_accept_shutdown(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 0};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_accept, (void *)&args);
+
+    sleep(2);
+    _shutdown(fd, SHUT_RDWR);
+
+    pthread_join(thread, NULL);
+
+    _close(fd);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+struct {
+    char *name;
+    int (*ptr)(int);
+} action_table[]  = {
+    {"accept_shutdown", do_accept_shutdown},
+    {NULL, NULL},
+};
+
+struct {
+    char *name;
+    enum sock_type type;
+} type_table[]  = {
+    {"unix", UNIX},
+    {"rfcomm", RFCOMM},
+    {"sco", SCO},
+    {"l2cap", L2CAP},
+    {"tcp", TCP},
+    {NULL, -1},
+};
+
+static void usage() {
+    int i;
+
+    printf("sock_shutdown_test TYPE ACTION\n");
+    printf("\nTYPE:\n");
+    for (i = 0; type_table[i].name; i++) {
+        printf("\t%s\n", type_table[i].name);
+    }
+    printf("\nACTION:\n");
+    for (i = 0; action_table[i].name; i++) {
+        printf("\t%s\n", action_table[i].name);
+    }
+}
+
+int main(int argc, char **argv) {
+    int i;
+    int type = -1;
+
+    if (argc != 3) {
+        usage();
+        return -1;
+    }
+    for (i = 0; type_table[i].name; i++) {
+        if (!strcmp(argv[1], type_table[i].name)) {
+            type = type_table[i].type;
+            break;
+        }
+    }
+    if (type == -1) {
+        usage();
+        return -1;
+    }
+    for (i = 0; action_table[i].name; i++) {
+        if (!strcmp(argv[2], action_table[i].name)) {
+            printf("TYPE = %s ACTION = %s\n", type_table[type].name,
+                    action_table[i].name);
+            return (*action_table[i].ptr)(type);
+        }
+    }
+    usage();
+    return -1;
+}
diff --git a/tools/socktest.c b/tools/socktest.c
new file mode 100644 (file)
index 0000000..b0c45c5
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+** Copyright 2009 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/** socket testing  */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/l2cap.h>
+
+enum sock_type {
+    UNIX = 0,
+    RFCOMM,
+    SCO,
+    L2CAP,
+    TCP,
+};
+
+struct thread_args {
+    int fd;
+    int type;
+    int delay;
+};
+
+struct sockaddr_un  local_addr_un  = {AF_UNIX, "/data/foo"};
+struct sockaddr_rc  local_addr_rc  = {AF_BLUETOOTH, *BDADDR_ANY, 4};
+struct sockaddr_sco local_addr_sco = {AF_BLUETOOTH, *BDADDR_LOCAL};
+struct sockaddr_l2  local_addr_l2  = {AF_BLUETOOTH, htobs(0x1001), *BDADDR_ANY, 0};
+struct sockaddr_in  local_addr_in  = {AF_INET, 9999, {0}, {0}};
+
+struct sockaddr_un  remote_addr_un  ;
+struct sockaddr_rc  remote_addr_rc  ;
+struct sockaddr_sco remote_addr_sco ;
+struct sockaddr_l2  remote_addr_l2  ;
+struct sockaddr_in  remote_addr_in  ;
+
+static void print_events(int events) {
+    if (events & POLLIN) printf("POLLIN ");
+    if (events & POLLPRI) printf("POLLPRI ");
+    if (events & POLLOUT) printf("POLLOUT ");
+    if (events & POLLERR) printf("POLLERR ");
+    if (events & POLLHUP) printf("POLLHUP ");
+    if (events & POLLNVAL) printf("POLLNVAL ");
+    printf("\n");
+}
+
+static void print_fds(struct pollfd *ufds, nfds_t nfds) {
+    unsigned int i;
+    for (i=0; i<nfds; i++)
+        printf("%d ", ufds[i].fd);
+}
+
+static int _socket(int type) {
+    int ret;
+    int family = -1;
+    int typ = -1;
+    int protocol = -1;
+
+    switch (type) {
+    case UNIX:
+        family = PF_UNIX;
+        typ = SOCK_STREAM;
+        protocol = 0;
+        break;
+    case RFCOMM:
+        family = PF_BLUETOOTH;
+        typ = SOCK_STREAM;
+        protocol = BTPROTO_RFCOMM;
+        break;
+    case SCO:
+        family = PF_BLUETOOTH;
+        typ = SOCK_SEQPACKET;
+        protocol = BTPROTO_SCO;
+        break;
+    case L2CAP:
+        family = PF_BLUETOOTH;
+        typ = SOCK_SEQPACKET;
+        protocol = BTPROTO_L2CAP;
+        break;
+    case TCP:
+        family = PF_INET;
+        typ = SOCK_STREAM;
+        protocol = 0;
+        break;
+    }
+
+    printf("%ld: socket()\n", pthread_self());
+    ret = socket(family, typ, protocol);
+    printf("%ld: socket() = %d\n", pthread_self(), ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _close(int fd, int type) {
+    int ret;
+
+    printf("%ld: close(%d)\n", pthread_self(), fd);
+    ret = close(fd);
+    printf("%ld: close(%d) = %d\n", pthread_self(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _bind(int fd, int type) {
+    int len = 0;
+    int ret;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        unlink(local_addr_un.sun_path);
+        addr = (struct sockaddr *) &local_addr_un;
+        len = sizeof(local_addr_un);
+        break;
+    case RFCOMM:
+        addr = (struct sockaddr *) &local_addr_rc;
+        len = sizeof(local_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &local_addr_sco;
+        len = sizeof(local_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &local_addr_l2;
+        len = sizeof(local_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &local_addr_in;
+        len = sizeof(local_addr_in);
+        break;
+    }
+
+    printf("%ld: bind(%d)\n", pthread_self(), fd);
+    ret = bind(fd, addr, len);
+    printf("%ld: bind(%d) = %d\n", pthread_self(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _listen(int fd, int type) {
+    int ret;
+
+    printf("%ld: listen(%d)\n", pthread_self(), fd);
+    ret = listen(fd, 1);
+    printf("%ld: listen(%d) = %d\n", pthread_self(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _read(int fd) {
+    int ret;
+    char buf;
+
+    printf("%ld: read(%d)\n", pthread_self(), fd);
+    ret = read(fd, &buf, 1);
+    printf("%ld: read(%d) = %d [%d]\n", pthread_self(), fd, ret, (int)buf);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+
+static int _accept(int fd, int type) {
+    int ret;
+    int len;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        addr = (struct sockaddr *) &remote_addr_un;
+        len = sizeof(remote_addr_un);
+        break;
+    case RFCOMM:
+        addr = (struct sockaddr *) &remote_addr_rc;
+        len = sizeof(remote_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &remote_addr_sco;
+        len = sizeof(remote_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &remote_addr_l2;
+        len = sizeof(remote_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &remote_addr_in;
+        len = sizeof(remote_addr_in);
+        break;
+    }
+
+    printf("%ld: accept(%d)\n", pthread_self(), fd);
+    ret = accept(fd, addr, &len);
+    printf("%ld: accept(%d) = %d\n", pthread_self(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+    else {
+        printf("\tlen = %d\n", len);
+    }
+
+    return ret;
+}
+
+int get_bdaddr(const char *str, bdaddr_t *ba) {
+    char *d = ((char *)ba) + 5, *endp;
+    int i;
+    for(i = 0; i < 6; i++) {
+        *d-- = strtol(str, &endp, 16);
+        if (*endp != ':' && i != 5) {
+            memset(ba, 0, sizeof(bdaddr_t));
+            return -1;
+        }
+        str = endp + 1;
+    }
+    return 0;
+}
+
+static int _connect(int fd, int type) {
+    int ret;
+    int len = 0;
+    struct sockaddr *addr = NULL;
+
+    switch (type) {
+    case UNIX:
+        addr = (struct sockaddr *) &local_addr_un;
+        len = sizeof(local_addr_un);
+        break;
+    case RFCOMM:
+        get_bdaddr("00:11:22:33:44:55", &local_addr_rc.rc_bdaddr);
+        addr = (struct sockaddr *) &local_addr_rc;
+        len = sizeof(local_addr_rc);
+        break;
+    case SCO:
+        addr = (struct sockaddr *) &local_addr_sco;
+        len = sizeof(local_addr_sco);
+        break;
+    case L2CAP:
+        addr = (struct sockaddr *) &local_addr_l2;
+        len = sizeof(local_addr_l2);
+        break;
+    case TCP:
+        addr = (struct sockaddr *) &local_addr_in;
+        len = sizeof(local_addr_in);
+        break;
+    }
+
+    printf("%ld: connect(%d)\n", pthread_self(), fd);
+    ret = connect(fd, addr, len);
+    printf("%ld: connect(%d) = %d\n", pthread_self(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _write(int fd, int type) {
+    int ret;
+    char buf = 69;
+
+    printf("%ld: write(%d)\n", pthread_self(), fd);
+    ret = write(fd, &buf, 1);
+    printf("%ld: write(%d) = %d\n", pthread_self(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _shutdown(int fd, int how) {
+    int ret;
+
+    printf("%ld: shutdown(%d)\n", pthread_self(), fd);
+    ret = shutdown(fd, how);
+    printf("%ld: shutdown(%d) = %d\n", pthread_self(), fd, ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+
+    return ret;
+}
+
+static int _poll(struct pollfd *ufds, nfds_t nfds, int timeout) {
+    int ret;
+    unsigned int i;
+
+    printf("%ld: poll(", pthread_self());
+    print_fds(ufds, nfds);
+    printf(")\n");
+    ret = poll(ufds, nfds, timeout);
+    printf("%ld: poll() = %d\n", pthread_self(), ret);
+    if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
+    if (ret > 0) {
+        for (i=0; i<nfds; i++) {
+            if (ufds[i].revents) {
+                printf("\tfd %d ", ufds[i].fd); print_events(ufds[i].revents);
+            }
+        }
+    }
+    return ret;
+}
+
+static void thread_delay_close(struct thread_args *args) {
+    printf("%ld: START\n", pthread_self());
+    sleep(args->delay);
+    _close(args->fd, args->type);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_poll(void *args) {
+    int fd = (int)args;
+    struct pollfd pfd;
+    printf("%ld: START\n", pthread_self());
+    pfd.fd = fd;
+    pfd.events = 0;
+    _poll(&pfd, 1, -1);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_read(void *args) {
+    int fd = (int)args;
+    printf("%ld: START\n", pthread_self());
+    _read(fd);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_pollin(void *args) {
+    int fd = (int)args;
+    struct pollfd pfd;
+    printf("%ld: START\n", pthread_self());
+    pfd.fd = fd;
+    pfd.events = POLLIN;
+    _poll(&pfd, 1, -1);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_shutdown(int fd) {
+    printf("%ld: START\n", pthread_self());
+    sleep(4);
+    _shutdown(fd, SHUT_RDWR);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_accept(struct thread_args *args) {
+    printf("%ld: START\n", pthread_self());
+    sleep(args->delay);
+    _accept(args->fd, args->type);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_connect(struct thread_args *args) {
+    printf("%ld: START\n", pthread_self());
+    sleep(args->delay);
+    _connect(args->fd, args->type);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_delay_close_write(struct thread_args *args) {
+    printf("%ld: START\n", pthread_self());
+    sleep(args->delay);
+    _close(args->fd, args->type);
+    sleep(args->delay);
+    _write(args->fd, args->type);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_accept_write(struct thread_args *args) {
+    printf("%ld: START\n", pthread_self());
+    sleep(args->delay);
+    _accept(args->fd, args->type);
+    sleep(args->delay);
+    _write(args->fd, args->type);
+    printf("%ld: END\n", pthread_self());
+}
+
+static void thread_delay_connect(struct thread_args *args) {
+    printf("%ld: START\n", pthread_self());
+    sleep(args->delay);
+    args->fd = _socket(args->type);
+    _connect(args->fd, args->type);
+    printf("%ld: END\n", pthread_self());
+}
+
+static int do_accept_accept_accept(int type) {
+    int fd;
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    while (1) {
+        _accept(fd, type);
+    }
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_accept_and_close(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 1};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_delay_close, (void *)&args);
+
+    _accept(fd, type);
+
+    pthread_join(thread, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_accept_shutdown(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 0};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_accept, (void *)&args);
+
+    sleep(4);
+    _shutdown(fd, SHUT_RDWR);
+
+    pthread_join(thread, NULL);
+
+    _close(fd, type);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_connect_shutdown(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 0};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_connect, (void *)&args);
+
+    sleep(4);
+    _shutdown(fd, SHUT_RDWR);
+
+    pthread_join(thread, NULL);
+
+    _close(fd, type);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_connectnb_shutdown(int type) {
+    int fd;
+    int flags;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 0};
+
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    flags = fcntl(fd, F_GETFL);
+    if (flags == -1)
+        return -1;
+    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+        return -1;
+
+    _connect(fd, type);
+
+    sleep(1);
+    _shutdown(fd, SHUT_RDWR);
+
+    sleep(2);
+
+    _close(fd, type);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_connectnb_close(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 0};
+    int flags;
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    flags = fcntl(fd, F_GETFL);
+    if (flags == -1)
+        return -1;
+    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+        return -1;
+
+    _connect(fd, type);
+
+    sleep(2);
+
+    _close(fd, type);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+// accept in one thread. close then write in another
+static int do_accept_close_write(int type) {
+    int fd;
+    pthread_t thread;
+    struct thread_args args = {-1, type, 1};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    args.fd = fd;
+    pthread_create(&thread, NULL, (void *)thread_delay_close_write, (void *)&args);
+
+    _accept(fd, type);
+
+    pthread_join(thread, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+static int do_poll_poll_poll_shutdown(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_poll, (void *)fd);
+
+    sleep(1);
+
+    _shutdown(fd, SHUT_RDWR);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    _close(fd, type);
+
+    return 0;
+}
+
+static int do_poll_poll_poll_close(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_poll, (void *)fd);
+
+    sleep(1);
+
+    _close(fd, type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    return 0;
+}
+
+static int do_read_read_read_close(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_read, (void *)fd);
+
+    sleep(1);
+
+    _close(fd, type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    return 0;
+}
+
+static int do_read_read_read_shutdown(int type) {
+    const int MAX_T = 32;
+    int fd;
+    pthread_t t[MAX_T];
+    int i;
+
+    fd = _socket(type);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_create(&t[i], NULL, (void *)thread_read, (void *)fd);
+
+    sleep(1);
+
+    _shutdown(fd, SHUT_RDWR);
+
+    for (i=0; i<MAX_T; i++)
+        pthread_join(t[i], NULL);
+
+    _close(fd, type);
+
+    return 0;
+}
+
+static int do_connected_read1_shutdown1(int type) {
+    int fd1, fd2;
+    pthread_t t1;
+    pthread_t t2;
+    struct thread_args a1 = {-1, type, 0};
+    struct thread_args a2 = {-1, type, 2};
+
+    fd1 = _socket(type);
+    if (fd1 < 0) goto error;
+
+    if (_bind(fd1, type) < 0) goto error;
+
+    if (_listen(fd1, type) < 0) goto error;
+
+    a1.fd = fd1;
+    pthread_create(&t1, NULL, (void *)thread_accept_write, (void *)&a1);
+
+    fd2 = _socket(type);
+    if (_connect(fd2, type)) goto error;
+
+    pthread_create(&t2, NULL, (void *)thread_shutdown, (void *)&fd2);
+    
+    while (1) if (_read(fd2)) break;
+
+    pthread_join(t1, NULL);
+    pthread_join(t2, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+// accept in one thread, connect from two different threads
+static int do_accept_connect_connect(int type) {
+    int fd;
+    pthread_t t1;
+    pthread_t t2;
+    struct thread_args a1 = {-1, type, 1};
+    struct thread_args a2 = {-1, type, 2};
+
+    fd = _socket(type);
+    if (fd < 0) goto error;
+
+    if (_bind(fd, type) < 0) goto error;
+
+    if (_listen(fd, type) < 0) goto error;
+
+    pthread_create(&t1, NULL, (void *)thread_delay_connect, (void *)&a1);
+    pthread_create(&t2, NULL, (void *)thread_delay_connect, (void *)&a2);
+
+    _accept(fd, type);
+
+    pthread_join(t1, NULL);
+    pthread_join(t2, NULL);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+struct {
+    char *name;
+    int (*ptr)(int);
+} action_table[]  = {
+    {"accept_accept_accept", do_accept_accept_accept},
+    {"accept_and_close", do_accept_and_close},
+    {"accept_shutdown", do_accept_shutdown},
+    {"connect_shutdown", do_connect_shutdown},
+    {"connectnb_shutdown", do_connectnb_shutdown},
+    {"connectnb_close", do_connectnb_close},
+    {"accept_close_write", do_accept_close_write},
+    {"accept_connect_connect", do_accept_connect_connect},
+    {"poll_poll_poll_shutdown", do_poll_poll_poll_shutdown},
+    {"poll_poll_poll_close", do_poll_poll_poll_close},
+    {"read_read_read_shutdown", do_read_read_read_shutdown},
+    {"read_read_read_close", do_read_read_read_close},
+    {"connected_read1_shutdown1", do_connected_read1_shutdown1},
+    {NULL, NULL},
+};
+
+struct {
+    char *name;
+    enum sock_type type;
+} type_table[]  = {
+    {"unix", UNIX},
+    {"rfcomm", RFCOMM},
+    {"sco", SCO},
+    {"l2cap", L2CAP},
+    {"tcp", TCP},
+    {NULL, -1},
+};
+
+static void usage() {
+    int i;
+
+    printf("socktest TYPE ACTION\n");
+    printf("\nTYPE:\n");
+    for (i = 0; type_table[i].name; i++) {
+        printf("\t%s\n", type_table[i].name);
+    }
+    printf("\nACTION:\n");
+    for (i = 0; action_table[i].name; i++) {
+        printf("\t%s\n", action_table[i].name);
+    }
+}
+
+int main(int argc, char **argv) {
+    int i;
+    int type = -1;
+
+    if (argc != 3) {
+        usage();
+        return -1;
+    }
+    for (i = 0; type_table[i].name; i++) {
+        if (!strcmp(argv[1], type_table[i].name)) {
+            type = type_table[i].type;
+            break;
+        }
+    }
+    if (type == -1) {
+        usage();
+        return -1;
+    }
+    for (i = 0; action_table[i].name; i++) {
+        if (!strcmp(argv[2], action_table[i].name)) {
+            printf("TYPE = %s ACTION = %s\n", type_table[type].name,
+                    action_table[i].name);
+            return (*action_table[i].ptr)(type);
+        }
+    }
+    usage();
+    return -1;
+}