[PATCHES][HCIOPS] Initial hciops plugin implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Marcel/Johan/Luiz,

Based on the discussion on #bluez, i am sending an updated patchset.

Let me know what you think of it.

Cheers,
Alok.
From bd853f4c8a8a7ec49d8045f10c73b53338a87633 Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxxx>
Date: Tue, 21 Apr 2009 20:16:04 +0530
Subject: [PATCH 1/8] Initialing HCI raw socket plugin "hciops".

---
 plugins/Makefile.am |    6 +++++-
 plugins/hciops.c    |   43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 1 deletions(-)
 create mode 100644 plugins/hciops.c

diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index a1e933f..bbabf64 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -13,7 +13,7 @@ else
 service_plugins =
 endif
 
-plugin_LTLIBRARIES = hal.la $(netlink_plugins) $(service_plugins)
+plugin_LTLIBRARIES = hal.la hciops.la $(netlink_plugins) $(service_plugins)
 
 noinst_LTLIBRARIES = echo.la storage.la
 
@@ -21,6 +21,8 @@ echo_la_SOURCES = echo.c
 
 storage_la_SOURCES = storage.c
 
+hciops_la_SOURCES = hciops.c
+
 if NETLINK
 netlink_la_SOURCES = netlink.c
 
@@ -47,6 +49,7 @@ all-local:
 	@$(LN_S) -f $(top_srcdir)/network/.libs/network.so
 	@$(LN_S) -f .libs/service.so
 	@$(LN_S) -f .libs/hal.so
+	@$(LN_S) -f .libs/hciops.so
 
 clean-local:
 	@rm -f hal.so
@@ -55,3 +58,4 @@ clean-local:
 	@rm -f serial.so
 	@rm -f audio.so
 	@rm -f input.so
+	@rm -f hciops.so
diff --git a/plugins/hciops.c b/plugins/hciops.c
new file mode 100644
index 0000000..4f56443
--- /dev/null
+++ b/plugins/hciops.c
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@xxxxxxxxxxxx>
+ *  Copyright (C) 2009	     Alok Barsode <alok.barsode@xxxxxxxxxx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "logging.h"
+
+static int hciops_init(void)
+{
+	return 0;
+}
+
+static void hciops_exit(void)
+{
+}
+
+BLUETOOTH_PLUGIN_DEFINE(hciops, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hciops_init, hciops_exit)
-- 
1.5.6.3

From 3938033c5fc16766a551d9680770ca93e811e9d4 Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxxx>
Date: Tue, 21 Apr 2009 21:58:07 +0530
Subject: [PATCH 2/8] exporting device_event().

---
 src/hcid.h |    1 +
 src/main.c |   28 ++++++++++++++--------------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/src/hcid.h b/src/hcid.h
index 6c26157..236449d 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -68,6 +68,7 @@ struct main_opts {
 
 extern struct main_opts main_opts;
 
+void device_event(int event, int dev_id);
 void hci_req_queue_remove(int dev_id, bdaddr_t *dba);
 
 void start_security_manager(int hdev);
diff --git a/src/main.c b/src/main.c
index f39fc11..02b4ebf 100644
--- a/src/main.c
+++ b/src/main.c
@@ -556,30 +556,28 @@ static void init_defaults(void)
 		strcpy(main_opts.host_name, "noname");
 }
 
-static inline void device_event(GIOChannel *chan, evt_stack_internal *si)
+void device_event(int event, int dev_id)
 {
-	evt_si_device *sd = (void *) &si->data;
-
-	switch (sd->event) {
+	switch (event) {
 	case HCI_DEV_REG:
-		info("HCI dev %d registered", sd->dev_id);
-		device_devreg_setup(sd->dev_id, FALSE);
+		info("HCI dev %d registered", dev_id);
+		device_devreg_setup(dev_id, FALSE);
 		break;
 
 	case HCI_DEV_UNREG:
-		info("HCI dev %d unregistered", sd->dev_id);
-		manager_unregister_adapter(sd->dev_id);
+		info("HCI dev %d unregistered", dev_id);
+		manager_unregister_adapter(dev_id);
 		break;
 
 	case HCI_DEV_UP:
-		info("HCI dev %d up", sd->dev_id);
-		device_devup_setup(sd->dev_id);
+		info("HCI dev %d up", dev_id);
+		device_devup_setup(dev_id);
 		break;
 
 	case HCI_DEV_DOWN:
-		info("HCI dev %d down", sd->dev_id);
-		manager_stop_adapter(sd->dev_id);
-		stop_security_manager(sd->dev_id);
+		info("HCI dev %d down", dev_id);
+		manager_stop_adapter(dev_id);
+		stop_security_manager(dev_id);
 		break;
 	}
 }
@@ -589,6 +587,7 @@ static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
 {
 	unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
 	evt_stack_internal *si;
+	evt_si_device *sd;
 	hci_event_hdr *eh;
 	int type;
 	size_t len;
@@ -620,7 +619,8 @@ static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
 	si = (evt_stack_internal *) ptr;
 	switch (si->type) {
 	case EVT_SI_DEVICE:
-		device_event(chan, si);
+		sd = (void *) &si->data;
+		device_event(sd->event, sd->dev_id);
 		break;
 	}
 
-- 
1.5.6.3

From a90b9364238d0f2edd763a3db669810c43bd8e96 Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxxx>
Date: Thu, 23 Apr 2009 16:42:16 +0530
Subject: [PATCH 3/8] Modifying device_devreg_setup. Check if device is up in device_devreg_setup instead of using devup parameter.

---
 src/main.c |   13 ++++++++-----
 1 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/main.c b/src/main.c
index 02b4ebf..d12edc5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -477,9 +477,10 @@ fail:
 	exit(1);
 }
 
-static void device_devreg_setup(int dev_id, gboolean devup)
+static void device_devreg_setup(int dev_id)
 {
 	struct hci_dev_info di;
+	gboolean devup;
 
 	init_device(dev_id);
 
@@ -488,6 +489,8 @@ static void device_devreg_setup(int dev_id, gboolean devup)
 	if (hci_devinfo(dev_id, &di) < 0)
 		return;
 
+	devup = hci_test_bit(HCI_UP, &di.flags);
+
 	if (!hci_test_bit(HCI_RAW, &di.flags))
 		manager_register_adapter(dev_id, devup);
 }
@@ -528,10 +531,10 @@ static void init_all_devices(int ctl)
 	for (i = 0; i < dl->dev_num; i++, dr++) {
 		gboolean devup;
 
-		devup = hci_test_bit(HCI_UP, &dr->dev_opt);
-
 		info("HCI dev %d registered", dr->dev_id);
-		device_devreg_setup(dr->dev_id, devup);
+		device_devreg_setup(dr->dev_id);
+
+		devup = hci_test_bit(HCI_UP, &dr->dev_opt);
 		if (devup) {
 			info("HCI dev %d already up", dr->dev_id);
 			device_devup_setup(dr->dev_id);
@@ -561,7 +564,7 @@ void device_event(int event, int dev_id)
 	switch (event) {
 	case HCI_DEV_REG:
 		info("HCI dev %d registered", dev_id);
-		device_devreg_setup(dev_id, FALSE);
+		device_devreg_setup(dev_id);
 		break;
 
 	case HCI_DEV_UNREG:
-- 
1.5.6.3

From 0d891eece965c0197d3ddb45336a646dcc227994 Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxxx>
Date: Thu, 23 Apr 2009 16:46:41 +0530
Subject: [PATCH 4/8] Using device_event to register and setup already known devices.

---
 src/main.c |    9 +++------
 1 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/main.c b/src/main.c
index d12edc5..93dbf71 100644
--- a/src/main.c
+++ b/src/main.c
@@ -531,14 +531,11 @@ static void init_all_devices(int ctl)
 	for (i = 0; i < dl->dev_num; i++, dr++) {
 		gboolean devup;
 
-		info("HCI dev %d registered", dr->dev_id);
-		device_devreg_setup(dr->dev_id);
+		device_event(HCI_DEV_REG, dr->dev_id);
 
 		devup = hci_test_bit(HCI_UP, &dr->dev_opt);
-		if (devup) {
-			info("HCI dev %d already up", dr->dev_id);
-			device_devup_setup(dr->dev_id);
-		}
+		if (devup)
+			device_event(HCI_DEV_UP, dr->dev_id);
 	}
 
 	g_free(dl);
-- 
1.5.6.3

From 1763faa8262fdd65bb0fd5db6e6732bf89d5f8bb Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxxx>
Date: Thu, 23 Apr 2009 17:35:02 +0530
Subject: [PATCH 5/8] Initializing hciops plugin. Adding IO event handing.

---
 plugins/hciops.c |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/main.c       |  122 +------------------------------------------------
 2 files changed, 134 insertions(+), 122 deletions(-)

diff --git a/plugins/hciops.c b/plugins/hciops.c
index 4f56443..32f82d0 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -24,17 +24,149 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
 
+#include <glib.h>
+
+#include "hcid.h"
 #include "plugin.h"
 #include "logging.h"
 
-static int hciops_init(void)
+
+static int init_all_devices(int ctl)
 {
+	struct hci_dev_list_req *dl;
+	struct hci_dev_req *dr;
+	int i;
+
+	dl = g_try_malloc0(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
+	if (!dl) {
+		info("Can't allocate devlist buffer: %s (%d)",
+							strerror(errno), errno);
+		return errno;
+	}
+
+	dl->dev_num = HCI_MAX_DEV;
+	dr = dl->dev_req;
+
+	if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
+		info("Can't get device list: %s (%d)",
+							strerror(errno), errno);
+		return errno;
+	}
+
+	for (i = 0; i < dl->dev_num; i++, dr++) {
+		gboolean devup;
+
+		device_event(HCI_DEV_REG, dr->dev_id);
+
+		devup = hci_test_bit(HCI_UP, &dr->dev_opt);
+		if (devup)
+			device_event(HCI_DEV_UP, dr->dev_id);
+	}
+
+	g_free(dl);
 	return 0;
 }
 
+static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
+	evt_stack_internal *si;
+	evt_si_device *sd;
+	hci_event_hdr *eh;
+	int type;
+	size_t len;
+	GIOError err;
+
+	ptr = buf;
+
+	err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
+	if (err) {
+		if (err == G_IO_ERROR_AGAIN)
+			return TRUE;
+
+		error("Read from control socket failed: %s (%d)",
+							strerror(errno), errno);
+		return FALSE;
+	}
+
+	type = *ptr++;
+
+	if (type != HCI_EVENT_PKT)
+		return TRUE;
+
+	eh = (hci_event_hdr *) ptr;
+	if (eh->evt != EVT_STACK_INTERNAL)
+		return TRUE;
+
+	ptr += HCI_EVENT_HDR_SIZE;
+
+	si = (evt_stack_internal *) ptr;
+	switch (si->type) {
+	case EVT_SI_DEVICE:
+		sd = (void *) &si->data;
+		device_event(sd->event, sd->dev_id);
+		break;
+	}
+
+	return TRUE;
+}
+
+static int hciops_init(void)
+{
+	struct sockaddr_hci addr;
+	struct hci_filter flt;
+	GIOChannel *ctl_io;
+	int sock;
+
+	/* Create and bind HCI socket */
+	sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+	if (sock < 0) {
+		error("Can't open HCI socket: %s (%d)", strerror(errno),
+								errno);
+		return errno;
+	}
+
+	/* Set filter */
+	hci_filter_clear(&flt);
+	hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+	hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
+	if (setsockopt(sock, SOL_HCI, HCI_FILTER, &flt,
+							sizeof(flt)) < 0) {
+		error("Can't set filter: %s (%d)", strerror(errno), errno);
+		return errno;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = HCI_DEV_NONE;
+	if (bind(sock, (struct sockaddr *) &addr,
+							sizeof(addr)) < 0) {
+		error("Can't bind HCI socket: %s (%d)",
+							strerror(errno), errno);
+		return errno;
+	}
+
+	ctl_io = g_io_channel_unix_new(sock);
+	g_io_channel_set_close_on_unref(ctl_io, TRUE);
+
+	g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL);
+
+	g_io_channel_unref(ctl_io);
+
+	/* Initialize already connected devices */
+	return init_all_devices(sock);
+}
+
 static void hciops_exit(void)
 {
 }
diff --git a/src/main.c b/src/main.c
index 93dbf71..309d4bb 100644
--- a/src/main.c
+++ b/src/main.c
@@ -506,41 +506,6 @@ static void device_devup_setup(int dev_id)
 		stop_security_manager(dev_id);
 }
 
-static void init_all_devices(int ctl)
-{
-	struct hci_dev_list_req *dl;
-	struct hci_dev_req *dr;
-	int i;
-
-	dl = g_try_malloc0(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
-	if (!dl) {
-		info("Can't allocate devlist buffer: %s (%d)",
-							strerror(errno), errno);
-		exit(1);
-	}
-
-	dl->dev_num = HCI_MAX_DEV;
-	dr = dl->dev_req;
-
-	if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
-		info("Can't get device list: %s (%d)",
-							strerror(errno), errno);
-		exit(1);
-	}
-
-	for (i = 0; i < dl->dev_num; i++, dr++) {
-		gboolean devup;
-
-		device_event(HCI_DEV_REG, dr->dev_id);
-
-		devup = hci_test_bit(HCI_UP, &dr->dev_opt);
-		if (devup)
-			device_event(HCI_DEV_UP, dr->dev_id);
-	}
-
-	g_free(dl);
-}
-
 static void init_defaults(void)
 {
 	/* Default HCId settings */
@@ -582,51 +547,6 @@ void device_event(int event, int dev_id)
 	}
 }
 
-static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
-								gpointer data)
-{
-	unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
-	evt_stack_internal *si;
-	evt_si_device *sd;
-	hci_event_hdr *eh;
-	int type;
-	size_t len;
-	GIOError err;
-
-	ptr = buf;
-
-	err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
-	if (err) {
-		if (err == G_IO_ERROR_AGAIN)
-			return TRUE;
-
-		error("Read from control socket failed: %s (%d)",
-							strerror(errno), errno);
-		return FALSE;
-	}
-
-	type = *ptr++;
-
-	if (type != HCI_EVENT_PKT)
-		return TRUE;
-
-	eh = (hci_event_hdr *) ptr;
-	if (eh->evt != EVT_STACK_INTERNAL)
-		return TRUE;
-
-	ptr += HCI_EVENT_HDR_SIZE;
-
-	si = (evt_stack_internal *) ptr;
-	switch (si->type) {
-	case EVT_SI_DEVICE:
-		sd = (void *) &si->data;
-		device_event(sd->event, sd->dev_id);
-		break;
-	}
-
-	return TRUE;
-}
-
 static GMainLoop *event_loop;
 
 static void sig_term(int sig)
@@ -655,10 +575,8 @@ int main(int argc, char *argv[])
 {
 	GOptionContext *context;
 	GError *err = NULL;
-	struct sockaddr_hci addr;
-	struct hci_filter flt;
 	struct sigaction sa;
-	GIOChannel *ctl_io, *child_io;
+	GIOChannel *child_io;
 	uint16_t mtu = 0;
 	GKeyFile *config;
 
@@ -706,34 +624,6 @@ int main(int argc, char *argv[])
 		enable_debug();
 	}
 
-	/* Create and bind HCI socket */
-	main_opts.sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
-	if (main_opts.sock < 0) {
-		error("Can't open HCI socket: %s (%d)", strerror(errno),
-								errno);
-		exit(1);
-	}
-
-	/* Set filter */
-	hci_filter_clear(&flt);
-	hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
-	hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
-	if (setsockopt(main_opts.sock, SOL_HCI, HCI_FILTER, &flt,
-							sizeof(flt)) < 0) {
-		error("Can't set filter: %s (%d)", strerror(errno), errno);
-		exit(1);
-	}
-
-	memset(&addr, 0, sizeof(addr));
-	addr.hci_family = AF_BLUETOOTH;
-	addr.hci_dev = HCI_DEV_NONE;
-	if (bind(main_opts.sock, (struct sockaddr *) &addr,
-							sizeof(addr)) < 0) {
-		error("Can't bind HCI socket: %s (%d)",
-							strerror(errno), errno);
-		exit(1);
-	}
-
 	config = load_config(CONFIGDIR "/main.conf");
 
 	parse_config(config);
@@ -768,16 +658,6 @@ int main(int argc, char *argv[])
 
 	event_loop = g_main_loop_new(NULL, FALSE);
 
-	ctl_io = g_io_channel_unix_new(main_opts.sock);
-	g_io_channel_set_close_on_unref(ctl_io, TRUE);
-
-	g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL);
-
-	g_io_channel_unref(ctl_io);
-
-	/* Initialize already connected devices */
-	init_all_devices(main_opts.sock);
-
 	starting = FALSE;
 
 	manager_startup_complete();
-- 
1.5.6.3

From dabb6568ca87e6eff9a0ef8d6da555d5050b6054 Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxx>
Date: Fri, 24 Apr 2009 15:48:58 +0530
Subject: [PATCH 6/8] Moving all adapter initialization code to hciops plugin.

---
 plugins/hciops.c |  248 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/hcid.h       |   11 +++
 src/main.c       |  249 +-----------------------------------------------------
 3 files changed, 259 insertions(+), 249 deletions(-)

diff --git a/plugins/hciops.c b/plugins/hciops.c
index 32f82d0..25f5171 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -24,10 +24,19 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
+
+#include <stdio.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <sys/wait.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
@@ -35,10 +44,235 @@
 
 #include <glib.h>
 
+#include <dbus/dbus.h>
+
 #include "hcid.h"
+#include "sdpd.h"
+#include "adapter.h"
 #include "plugin.h"
 #include "logging.h"
+#include "manager.h"
+#include "storage.h"
+
+static int child_pipe[2];
+
+static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data)
+{
+	int status, fd = g_io_channel_unix_get_fd(io);
+	pid_t child_pid;
+
+	if (read(fd, &child_pid, sizeof(child_pid)) != sizeof(child_pid)) {
+		error("child_exit: unable to read child pid from pipe");
+		return TRUE;
+	}
+
+	if (waitpid(child_pid, &status, 0) != child_pid)
+		error("waitpid(%d) failed", child_pid);
+	else
+		debug("child %d exited", child_pid);
+
+	return TRUE;
+}
+
+static void at_child_exit(void)
+{
+	pid_t pid = getpid();
+
+	if (write(child_pipe[1], &pid, sizeof(pid)) != sizeof(pid))
+		error("unable to write to child pipe");
+}
+
+static void configure_device(int dev_id)
+{
+	struct hci_dev_info di;
+	uint16_t policy;
+	int dd;
+
+	if (hci_devinfo(dev_id, &di) < 0)
+		return;
+
+	if (hci_test_bit(HCI_RAW, &di.flags))
+		return;
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		error("Can't open device hci%d: %s (%d)",
+						dev_id, strerror(errno), errno);
+		return;
+	}
+
+	/* Set device name */
+	if ((main_opts.flags & (1 << HCID_SET_NAME)) && main_opts.name) {
+		change_local_name_cp cp;
+
+		memset(cp.name, 0, sizeof(cp.name));
+		expand_name((char *) cp.name, sizeof(cp.name),
+						main_opts.name, dev_id);
+
+		hci_send_cmd(dd, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
+					CHANGE_LOCAL_NAME_CP_SIZE, &cp);
+	}
+
+	/* Set device class */
+	if ((main_opts.flags & (1 << HCID_SET_CLASS))) {
+		write_class_of_dev_cp cp;
+		uint32_t class;
+		uint8_t cls[3];
+
+		if (read_local_class(&di.bdaddr, cls) < 0) {
+			class = htobl(main_opts.class);
+			cls[2] = get_service_classes(&di.bdaddr);
+			memcpy(cp.dev_class, &class, 3);
+		} else {
+			if (!(main_opts.scan & SCAN_INQUIRY))
+				cls[1] &= 0xdf; /* Clear discoverable bit */
+			cls[2] = get_service_classes(&di.bdaddr);
+			memcpy(cp.dev_class, cls, 3);
+		}
+
+		hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
+					WRITE_CLASS_OF_DEV_CP_SIZE, &cp);
+	}
+
+	/* Set page timeout */
+	if ((main_opts.flags & (1 << HCID_SET_PAGETO))) {
+		write_page_timeout_cp cp;
+
+		cp.timeout = htobs(main_opts.pageto);
+		hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_PAGE_TIMEOUT,
+					WRITE_PAGE_TIMEOUT_CP_SIZE, &cp);
+	}
+
+	/* Set default link policy */
+	policy = htobs(main_opts.link_policy);
+	hci_send_cmd(dd, OGF_LINK_POLICY,
+				OCF_WRITE_DEFAULT_LINK_POLICY, 2, &policy);
+
+	hci_close_dev(dd);
+}
+
+static void init_device(int dev_id)
+{
+	struct hci_dev_req dr;
+	struct hci_dev_info di;
+	pid_t pid;
+	int dd;
+
+	/* Do initialization in the separate process */
+	pid = fork();
+	switch (pid) {
+		case 0:
+			atexit(at_child_exit);
+			break;
+		case -1:
+			error("Fork failed. Can't init device hci%d: %s (%d)",
+					dev_id, strerror(errno), errno);
+		default:
+			debug("child %d forked", pid);
+			return;
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		error("Can't open device hci%d: %s (%d)",
+					dev_id, strerror(errno), errno);
+		exit(1);
+	}
 
+	memset(&dr, 0, sizeof(dr));
+	dr.dev_id = dev_id;
+
+	/* Set link mode */
+	dr.dev_opt = main_opts.link_mode;
+	if (ioctl(dd, HCISETLINKMODE, (unsigned long) &dr) < 0) {
+		error("Can't set link mode on hci%d: %s (%d)",
+					dev_id, strerror(errno), errno);
+	}
+
+	/* Set link policy */
+	dr.dev_opt = main_opts.link_policy;
+	if (ioctl(dd, HCISETLINKPOL, (unsigned long) &dr) < 0 &&
+							errno != ENETDOWN) {
+		error("Can't set link policy on hci%d: %s (%d)",
+					dev_id, strerror(errno), errno);
+	}
+
+	/* Start HCI device */
+	if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
+		error("Can't init device hci%d: %s (%d)",
+					dev_id, strerror(errno), errno);
+		goto fail;
+	}
+
+	if (hci_devinfo(dev_id, &di) < 0)
+		goto fail;
+
+	if (hci_test_bit(HCI_RAW, &di.flags))
+		goto done;
+
+done:
+	hci_close_dev(dd);
+	exit(0);
+
+fail:
+	hci_close_dev(dd);
+	exit(1);
+}
+
+static void device_devreg_setup(int dev_id)
+{
+	struct hci_dev_info di;
+	gboolean devup;
+
+	init_device(dev_id);
+
+	memset(&di, 0, sizeof(di));
+
+	if (hci_devinfo(dev_id, &di) < 0)
+		return;
+
+	devup = hci_test_bit(HCI_UP, &di.flags);
+
+	if (!hci_test_bit(HCI_RAW, &di.flags))
+		manager_register_adapter(dev_id, devup);
+}
+
+static void device_devup_setup(int dev_id)
+{
+	configure_device(dev_id);
+
+	start_security_manager(dev_id);
+
+	/* Return value 1 means ioctl(DEVDOWN) was performed */
+	if (manager_start_adapter(dev_id) == 1)
+		stop_security_manager(dev_id);
+}
+
+void device_event(int event, int dev_id)
+{
+	switch (event) {
+	case HCI_DEV_REG:
+		info("HCI dev %d registered", dev_id);
+		device_devreg_setup(dev_id);
+		break;
+
+	case HCI_DEV_UNREG:
+		info("HCI dev %d unregistered", dev_id);
+		manager_unregister_adapter(dev_id);
+		break;
+
+	case HCI_DEV_UP:
+		info("HCI dev %d up", dev_id);
+		device_devup_setup(dev_id);
+		break;
+
+	case HCI_DEV_DOWN:
+		info("HCI dev %d down", dev_id);
+		manager_stop_adapter(dev_id);
+		stop_security_manager(dev_id);
+		break;
+	}
+}
 
 static int init_all_devices(int ctl)
 {
@@ -125,9 +359,21 @@ static int hciops_init(void)
 {
 	struct sockaddr_hci addr;
 	struct hci_filter flt;
-	GIOChannel *ctl_io;
+	GIOChannel *ctl_io, *child_io;;
 	int sock;
 
+	if (pipe(child_pipe) < 0) {
+		error("pipe(): %s (%d)", strerror(errno), errno);
+		return errno;
+	}
+
+	child_io = g_io_channel_unix_new(child_pipe[0]);
+	g_io_channel_set_close_on_unref(child_io, TRUE);
+	g_io_add_watch(child_io,
+			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+			child_exit, NULL);
+	g_io_channel_unref(child_io);
+
 	/* Create and bind HCI socket */
 	sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
 	if (sock < 0) {
diff --git a/src/hcid.h b/src/hcid.h
index 236449d..e6f85d3 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -42,6 +42,8 @@
 #define MODE_LIMITED		0x03
 #define MODE_UNKNOWN		0xff
 
+#define HCID_DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
+
 /* Timeout for hci_send_req (milliseconds) */
 #define HCI_REQ_TIMEOUT		5000
 
@@ -66,8 +68,17 @@ struct main_opts {
 	int		sock;
 };
 
+enum {
+	HCID_SET_NAME,
+	HCID_SET_CLASS,
+	HCID_SET_PAGETO,
+	HCID_SET_DISCOVTO,
+};
+
 extern struct main_opts main_opts;
 
+char *expand_name(char *dst, int size, char *str, int dev_id);
+
 void device_event(int event, int dev_id);
 void hci_req_queue_remove(int dev_id, bdaddr_t *dba);
 
diff --git a/src/main.c b/src/main.c
index 309d4bb..a5a728c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -28,17 +28,13 @@
 #endif
 
 #include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
-#include <signal.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
@@ -57,21 +53,9 @@
 #include "dbus-common.h"
 #include "agent.h"
 #include "manager.h"
-#include "storage.h"
-
-#define HCID_DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
-
-enum {
-	HCID_SET_NAME,
-	HCID_SET_CLASS,
-	HCID_SET_PAGETO,
-	HCID_SET_DISCOVTO,
-};
 
 struct main_opts main_opts;
 
-static int child_pipe[2];
-
 static gboolean starting = TRUE;
 
 static GKeyFile *load_config(const char *file)
@@ -260,7 +244,7 @@ static void update_service_classes(const bdaddr_t *bdaddr, uint8_t value)
  * Device name expansion
  *   %d - device id
  */
-static char *expand_name(char *dst, int size, char *str, int dev_id)
+char *expand_name(char *dst, int size, char *str, int dev_id)
 {
 	register int sp, np, olen;
 	char *opt, buf[10];
@@ -314,198 +298,6 @@ static char *expand_name(char *dst, int size, char *str, int dev_id)
 	return dst;
 }
 
-static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data)
-{
-	int status, fd = g_io_channel_unix_get_fd(io);
-	pid_t child_pid;
-
-	if (read(fd, &child_pid, sizeof(child_pid)) != sizeof(child_pid)) {
-		error("child_exit: unable to read child pid from pipe");
-		return TRUE;
-	}
-
-	if (waitpid(child_pid, &status, 0) != child_pid)
-		error("waitpid(%d) failed", child_pid);
-	else
-		debug("child %d exited", child_pid);
-
-	return TRUE;
-}
-
-static void at_child_exit(void)
-{
-	pid_t pid = getpid();
-
-	if (write(child_pipe[1], &pid, sizeof(pid)) != sizeof(pid))
-		error("unable to write to child pipe");
-}
-
-static void configure_device(int dev_id)
-{
-	struct hci_dev_info di;
-	uint16_t policy;
-	int dd;
-
-	if (hci_devinfo(dev_id, &di) < 0)
-		return;
-
-	if (hci_test_bit(HCI_RAW, &di.flags))
-		return;
-
-	dd = hci_open_dev(dev_id);
-	if (dd < 0) {
-		error("Can't open device hci%d: %s (%d)",
-						dev_id, strerror(errno), errno);
-		return;
-	}
-
-	/* Set device name */
-	if ((main_opts.flags & (1 << HCID_SET_NAME)) && main_opts.name) {
-		change_local_name_cp cp;
-
-		memset(cp.name, 0, sizeof(cp.name));
-		expand_name((char *) cp.name, sizeof(cp.name),
-						main_opts.name, dev_id);
-
-		hci_send_cmd(dd, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
-					CHANGE_LOCAL_NAME_CP_SIZE, &cp);
-	}
-
-	/* Set device class */
-	if ((main_opts.flags & (1 << HCID_SET_CLASS))) {
-		write_class_of_dev_cp cp;
-		uint32_t class;
-		uint8_t cls[3];
-
-		if (read_local_class(&di.bdaddr, cls) < 0) {
-			class = htobl(main_opts.class);
-			cls[2] = get_service_classes(&di.bdaddr);
-			memcpy(cp.dev_class, &class, 3);
-		} else {
-			if (!(main_opts.scan & SCAN_INQUIRY))
-				cls[1] &= 0xdf; /* Clear discoverable bit */
-			cls[2] = get_service_classes(&di.bdaddr);
-			memcpy(cp.dev_class, cls, 3);
-		}
-
-		hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
-					WRITE_CLASS_OF_DEV_CP_SIZE, &cp);
-	}
-
-	/* Set page timeout */
-	if ((main_opts.flags & (1 << HCID_SET_PAGETO))) {
-		write_page_timeout_cp cp;
-
-		cp.timeout = htobs(main_opts.pageto);
-		hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_PAGE_TIMEOUT,
-					WRITE_PAGE_TIMEOUT_CP_SIZE, &cp);
-	}
-
-	/* Set default link policy */
-	policy = htobs(main_opts.link_policy);
-	hci_send_cmd(dd, OGF_LINK_POLICY,
-				OCF_WRITE_DEFAULT_LINK_POLICY, 2, &policy);
-
-	hci_close_dev(dd);
-}
-
-static void init_device(int dev_id)
-{
-	struct hci_dev_req dr;
-	struct hci_dev_info di;
-	pid_t pid;
-	int dd;
-
-	/* Do initialization in the separate process */
-	pid = fork();
-	switch (pid) {
-		case 0:
-			atexit(at_child_exit);
-			break;
-		case -1:
-			error("Fork failed. Can't init device hci%d: %s (%d)",
-					dev_id, strerror(errno), errno);
-		default:
-			debug("child %d forked", pid);
-			return;
-	}
-
-	dd = hci_open_dev(dev_id);
-	if (dd < 0) {
-		error("Can't open device hci%d: %s (%d)",
-					dev_id, strerror(errno), errno);
-		exit(1);
-	}
-
-	memset(&dr, 0, sizeof(dr));
-	dr.dev_id = dev_id;
-
-	/* Set link mode */
-	dr.dev_opt = main_opts.link_mode;
-	if (ioctl(dd, HCISETLINKMODE, (unsigned long) &dr) < 0) {
-		error("Can't set link mode on hci%d: %s (%d)",
-					dev_id, strerror(errno), errno);
-	}
-
-	/* Set link policy */
-	dr.dev_opt = main_opts.link_policy;
-	if (ioctl(dd, HCISETLINKPOL, (unsigned long) &dr) < 0 &&
-							errno != ENETDOWN) {
-		error("Can't set link policy on hci%d: %s (%d)",
-					dev_id, strerror(errno), errno);
-	}
-
-	/* Start HCI device */
-	if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
-		error("Can't init device hci%d: %s (%d)",
-					dev_id, strerror(errno), errno);
-		goto fail;
-	}
-
-	if (hci_devinfo(dev_id, &di) < 0)
-		goto fail;
-
-	if (hci_test_bit(HCI_RAW, &di.flags))
-		goto done;
-
-done:
-	hci_close_dev(dd);
-	exit(0);
-
-fail:
-	hci_close_dev(dd);
-	exit(1);
-}
-
-static void device_devreg_setup(int dev_id)
-{
-	struct hci_dev_info di;
-	gboolean devup;
-
-	init_device(dev_id);
-
-	memset(&di, 0, sizeof(di));
-
-	if (hci_devinfo(dev_id, &di) < 0)
-		return;
-
-	devup = hci_test_bit(HCI_UP, &di.flags);
-
-	if (!hci_test_bit(HCI_RAW, &di.flags))
-		manager_register_adapter(dev_id, devup);
-}
-
-static void device_devup_setup(int dev_id)
-{
-	configure_device(dev_id);
-
-	start_security_manager(dev_id);
-
-	/* Return value 1 means ioctl(DEVDOWN) was performed */
-	if (manager_start_adapter(dev_id) == 1)
-		stop_security_manager(dev_id);
-}
-
 static void init_defaults(void)
 {
 	/* Default HCId settings */
@@ -521,32 +313,6 @@ static void init_defaults(void)
 		strcpy(main_opts.host_name, "noname");
 }
 
-void device_event(int event, int dev_id)
-{
-	switch (event) {
-	case HCI_DEV_REG:
-		info("HCI dev %d registered", dev_id);
-		device_devreg_setup(dev_id);
-		break;
-
-	case HCI_DEV_UNREG:
-		info("HCI dev %d unregistered", dev_id);
-		manager_unregister_adapter(dev_id);
-		break;
-
-	case HCI_DEV_UP:
-		info("HCI dev %d up", dev_id);
-		device_devup_setup(dev_id);
-		break;
-
-	case HCI_DEV_DOWN:
-		info("HCI dev %d down", dev_id);
-		manager_stop_adapter(dev_id);
-		stop_security_manager(dev_id);
-		break;
-	}
-}
-
 static GMainLoop *event_loop;
 
 static void sig_term(int sig)
@@ -576,7 +342,6 @@ int main(int argc, char *argv[])
 	GOptionContext *context;
 	GError *err = NULL;
 	struct sigaction sa;
-	GIOChannel *child_io;
 	uint16_t mtu = 0;
 	GKeyFile *config;
 
@@ -628,18 +393,6 @@ int main(int argc, char *argv[])
 
 	parse_config(config);
 
-	if (pipe(child_pipe) < 0) {
-		error("pipe(): %s (%d)", strerror(errno), errno);
-		exit(1);
-	}
-
-	child_io = g_io_channel_unix_new(child_pipe[0]);
-	g_io_channel_set_close_on_unref(child_io, TRUE);
-	g_io_add_watch(child_io,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			child_exit, NULL);
-	g_io_channel_unref(child_io);
-
 	agent_init();
 
 	if (hcid_dbus_init() < 0) {
-- 
1.5.6.3

From 68d889fc2ac063b89cddc09f755bf3a11c5cecda Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxx>
Date: Mon, 27 Apr 2009 15:38:03 +0530
Subject: [PATCH 7/8] Adding btd_register_adapter_ops framework.

---
 plugins/hciops.c |   16 +++++++++++++++-
 src/adapter.c    |   28 ++++++++++++++++++++++++++++
 src/adapter.h    |    8 ++++++++
 3 files changed, 51 insertions(+), 1 deletions(-)

diff --git a/plugins/hciops.c b/plugins/hciops.c
index 25f5171..26fa1dc 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -355,7 +355,7 @@ static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
 	return TRUE;
 }
 
-static int hciops_init(void)
+static int hci_init(void)
 {
 	struct sockaddr_hci addr;
 	struct hci_filter flt;
@@ -413,8 +413,22 @@ static int hciops_init(void)
 	return init_all_devices(sock);
 }
 
+static void hci_cleanup(void)
+{
+}
+
+static struct btd_adapter_ops hci_ops = {
+	.init	 = hci_init,
+	.cleanup = hci_cleanup,
+};
+
+static int hciops_init(void)
+{
+	return btd_register_adapter_ops(&hci_ops);
+}
 static void hciops_exit(void)
 {
+	btd_adapter_cleanup_ops(&hci_ops);
 }
 
 BLUETOOTH_PLUGIN_DEFINE(hciops, VERSION,
diff --git a/src/adapter.c b/src/adapter.c
index 40c5ab0..c41b933 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -73,6 +73,7 @@
 
 static DBusConnection *connection = NULL;
 static GSList *adapter_drivers = NULL;
+static struct btd_adapter_ops *adapter_ops = NULL;
 
 struct session_req {
 	struct btd_adapter	*adapter;
@@ -2905,6 +2906,33 @@ void btd_unregister_adapter_driver(struct btd_adapter_driver *driver)
 	adapter_drivers = g_slist_remove(adapter_drivers, driver);
 }
 
+int btd_register_adapter_ops(struct btd_adapter_ops *btd_adapter_ops)
+{
+	int err;
+
+	/* Already registered */
+	if (adapter_ops)
+		return 1;
+
+	if (btd_adapter_ops->init == NULL)
+		return -1;
+
+	err = btd_adapter_ops->init();
+	if (err < 0) {
+		error("btd_register_adapter_ops failed:%s (%d)", strerror(-err), -err);
+		return -1;
+	}
+
+	adapter_ops = btd_adapter_ops;
+
+	return 0;
+}
+
+void btd_adapter_cleanup_ops()
+{
+	adapter_ops->cleanup();
+}
+
 static void agent_auth_cb(struct agent *agent, DBusError *derr,
 							void *user_data)
 {
diff --git a/src/adapter.h b/src/adapter.h
index 8111d6f..4e4a3d2 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -147,3 +147,11 @@ const char *btd_adapter_any_request_path(void);
 void btd_adapter_any_release_path(void);
 gboolean adapter_is_pairable(struct btd_adapter *adapter);
 gboolean adapter_powering_down(struct btd_adapter *adapter);
+
+struct btd_adapter_ops {
+	int (*init)();
+	void (*cleanup)();
+};
+
+int btd_register_adapter_ops(struct btd_adapter_ops *btd_adapter_ops);
+void btd_adapter_cleanup_ops();
-- 
1.5.6.3

From fb33d026cdc31e6aa9ed0de52478e9cfb9b7e29e Mon Sep 17 00:00:00 2001
From: Alok Barsode <alok.barsode@xxxxxxxxx>
Date: Mon, 27 Apr 2009 16:31:00 +0530
Subject: [PATCH 8/8] Exporting init_known_adapters from hciops plugin.

---
 plugins/hciops.c |   16 ++++++++--------
 src/adapter.c    |    8 ++++++++
 src/adapter.h    |    2 ++
 src/main.c       |    2 ++
 4 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/plugins/hciops.c b/plugins/hciops.c
index 26fa1dc..3cd3868 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -54,6 +54,7 @@
 #include "manager.h"
 #include "storage.h"
 
+static int sock;
 static int child_pipe[2];
 
 static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data)
@@ -273,8 +274,8 @@ void device_event(int event, int dev_id)
 		break;
 	}
 }
-
-static int init_all_devices(int ctl)
+ /* Initialize already connected devices */
+static int init_all_devices(void)
 {
 	struct hci_dev_list_req *dl;
 	struct hci_dev_req *dr;
@@ -290,7 +291,7 @@ static int init_all_devices(int ctl)
 	dl->dev_num = HCI_MAX_DEV;
 	dr = dl->dev_req;
 
-	if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
+	if (ioctl(sock, HCIGETDEVLIST, (void *) dl) < 0) {
 		info("Can't get device list: %s (%d)",
 							strerror(errno), errno);
 		return errno;
@@ -360,7 +361,6 @@ static int hci_init(void)
 	struct sockaddr_hci addr;
 	struct hci_filter flt;
 	GIOChannel *ctl_io, *child_io;;
-	int sock;
 
 	if (pipe(child_pipe) < 0) {
 		error("pipe(): %s (%d)", strerror(errno), errno);
@@ -409,8 +409,7 @@ static int hci_init(void)
 
 	g_io_channel_unref(ctl_io);
 
-	/* Initialize already connected devices */
-	return init_all_devices(sock);
+	return 0;
 }
 
 static void hci_cleanup(void)
@@ -418,8 +417,9 @@ static void hci_cleanup(void)
 }
 
 static struct btd_adapter_ops hci_ops = {
-	.init	 = hci_init,
-	.cleanup = hci_cleanup,
+	.init		     = hci_init,
+	.cleanup	     = hci_cleanup,
+	.init_known_adapters = init_all_devices,
 };
 
 static int hciops_init(void)
diff --git a/src/adapter.c b/src/adapter.c
index c41b933..d315698 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2933,6 +2933,14 @@ void btd_adapter_cleanup_ops()
 	adapter_ops->cleanup();
 }
 
+int init_known_adapters()
+{
+	if (!adapter_ops)
+		return -1;
+
+	return adapter_ops->init_known_adapters();
+}
+
 static void agent_auth_cb(struct agent *agent, DBusError *derr,
 							void *user_data)
 {
diff --git a/src/adapter.h b/src/adapter.h
index 4e4a3d2..c4b6793 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -151,7 +151,9 @@ gboolean adapter_powering_down(struct btd_adapter *adapter);
 struct btd_adapter_ops {
 	int (*init)();
 	void (*cleanup)();
+	int (*init_known_adapters)();
 };
 
 int btd_register_adapter_ops(struct btd_adapter_ops *btd_adapter_ops);
 void btd_adapter_cleanup_ops();
+int init_known_adapters();
diff --git a/src/main.c b/src/main.c
index a5a728c..346dc0c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -411,6 +411,8 @@ int main(int argc, char *argv[])
 
 	event_loop = g_main_loop_new(NULL, FALSE);
 
+	init_known_adapters();
+
 	starting = FALSE;
 
 	manager_startup_complete();
-- 
1.5.6.3


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux