diff --git a/arch/arm/mach-omap2/board-evt1a.c b/arch/arm/mach-omap2/board-evt1a.c index 908ee8b45bb..32b21c9fcc5 100644 --- a/arch/arm/mach-omap2/board-evt1a.c +++ b/arch/arm/mach-omap2/board-evt1a.c @@ -44,7 +44,7 @@ struct ti_st_plat_data wilink_pdata = { .nshutdown_gpio = 60, .dev_name = "/dev/ttyO1", .flow_cntrl = 1, - .baud_rate = 115200 // was 3000000, + .baud_rate = 3000000, }; static struct platform_device kim_wl127x_device = { diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 464539f58d2..af25f6a0412 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -43,7 +43,7 @@ #define HCI_NOTIFY_CONN_DEL 2 #define HCI_NOTIFY_VOICE_SETTING 3 -/* HCI device types */ +/* HCI bus types */ #define HCI_VIRTUAL 0 #define HCI_USB 1 #define HCI_PCCARD 2 @@ -53,8 +53,8 @@ #define HCI_SDIO 6 /* HCI controller types */ -#define HCI_BREDR 0x00 -#define HCI_80211 0x01 +#define HCI_BREDR 0x00 +#define HCI_80211 0x01 /* HCI device quirks */ enum { @@ -1043,3 +1043,4 @@ struct hci_inquiry_req { #define IREQ_CACHE_FLUSH 0x0001 #endif /* __HCI_H */ + diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 43cfb3b3180..607bda4f0e0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -107,7 +107,7 @@ struct hci_dev { unsigned long acl_last_tx; unsigned long sco_last_tx; - struct workqueue_struct *workqueue; + struct workqueue_struct *workqueue; struct tasklet_struct cmd_task; struct tasklet_struct rx_task; @@ -137,7 +137,7 @@ struct hci_dev { atomic_t promisc; - struct dentry *debugfs; + struct dentry *debugfs; struct device *parent; struct device dev; @@ -250,6 +250,7 @@ enum { HCI_CONN_ENCRYPT_PEND, HCI_CONN_RSWITCH_PEND, HCI_CONN_MODE_CHANGE_PEND, + HCI_CONN_SCO_SETUP_PEND, }; static inline void hci_conn_hash_init(struct hci_dev *hdev) @@ -330,6 +331,7 @@ void hci_acl_connect(struct hci_conn *conn); void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_add_sco(struct hci_conn *conn, __u16 handle); void hci_setup_sync(struct hci_conn *conn, __u16 handle); +void hci_sco_setup(struct hci_conn *conn, __u8 status); struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, __u16 pkt_type, bdaddr_t *dst); @@ -378,17 +380,17 @@ static inline void hci_conn_put(struct hci_conn *conn) /* ----- HCI tasks ----- */ static inline void hci_sched_cmd(struct hci_dev *hdev) { - tasklet_schedule(&hdev->cmd_task); + tasklet_schedule(&hdev->cmd_task); } static inline void hci_sched_rx(struct hci_dev *hdev) { - tasklet_schedule(&hdev->rx_task); + tasklet_schedule(&hdev->rx_task); } static inline void hci_sched_tx(struct hci_dev *hdev) { - tasklet_schedule(&hdev->tx_task); + tasklet_schedule(&hdev->tx_task); } /* ----- HCI Devices ----- */ @@ -446,27 +448,28 @@ int hci_inquiry(void __user *arg); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); /* Receive frame from HCI drivers */ -static inline int hci_recv_frame(struct sk_buff *skb) -{ - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - if (!hdev || (!test_bit(HCI_UP, &hdev->flags) - && !test_bit(HCI_INIT, &hdev->flags))) { - kfree_skb(skb); - return -ENXIO; - } - - /* Incomming skb */ - bt_cb(skb)->incoming = 1; - - /* Time stamp */ - __net_timestamp(skb); - - /* Queue frame for rx task */ - skb_queue_tail(&hdev->rx_q, skb); - hci_sched_rx(hdev); - return 0; + static inline int hci_recv_frame(struct sk_buff *skb) + { + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + if (!hdev || (!test_bit(HCI_UP, &hdev->flags) + && !test_bit(HCI_INIT, &hdev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Incomming skb */ + bt_cb(skb)->incoming = 1; + + /* Time stamp */ + __net_timestamp(skb); + + /* Queue frame for rx task */ + skb_queue_tail(&hdev->rx_q, skb); + hci_sched_rx(hdev); + return 0; } +//int hci_recv_frame(struct sk_buff *skb); int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); int hci_register_sysfs(struct hci_dev *hdev); @@ -719,3 +722,4 @@ struct hci_sec_filter { void hci_req_complete(struct hci_dev *hdev, int result); #endif /* __HCI_CORE_H */ + diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 2f4d30fd122..a674a12e6db 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -155,6 +155,27 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); } +/* Device _must_ be locked */ +void hci_sco_setup(struct hci_conn *conn, __u8 status) +{ + struct hci_conn *sco = conn->link; + + BT_DBG("%p", conn); + + if (!sco) + return; + + if (!status) { + if (lmp_esco_capable(conn->hdev)) + hci_setup_sync(sco, conn->handle); + else + hci_add_sco(sco, conn->handle); + } else { + hci_proto_connect_cfm(sco, status); + hci_conn_del(sco); + } +} + static void hci_conn_timeout(unsigned long arg) { struct hci_conn *conn = (void *) arg; @@ -369,6 +390,11 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, acl->sec_level = sec_level; acl->auth_type = auth_type; hci_acl_connect(acl); + } else { + if (acl->sec_level < sec_level) + acl->sec_level = sec_level; + if (acl->auth_type < auth_type) + acl->auth_type = auth_type; } if (type == ACL_LINK) @@ -391,10 +417,13 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, acl->power_save = 1; hci_conn_enter_active_mode(acl); - if (lmp_esco_capable(hdev)) - hci_setup_sync(sco, acl->handle); - else - hci_add_sco(sco, acl->handle); + if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) { + /* defer SCO setup until mode change completed */ + set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->pend); + return sco; + } + + hci_sco_setup(acl, 0x00); } return sco; @@ -732,3 +761,4 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg) return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; } + diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c6ce23e68f9..5f37f1f041a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -23,7 +23,6 @@ */ /* Bluetooth HCI core. */ - #include #include #include diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5343e0f664d..76a07a3b8e6 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -23,7 +23,6 @@ */ /* Bluetooth HCI event handling. */ - #include #include @@ -785,9 +784,13 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) + if (conn) { clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) + hci_sco_setup(conn, status); + } + hci_dev_unlock(hdev); } @@ -808,9 +811,13 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) + if (conn) { clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) + hci_sco_setup(conn, status); + } + hci_dev_unlock(hdev); } @@ -862,9 +869,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s struct hci_conn *conn; BT_DBG("%s", hdev->name); - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { if (ev->link_type != SCO_LINK) @@ -892,10 +897,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (test_bit(HCI_AUTH, &hdev->flags)) conn->link_mode |= HCI_LM_AUTH; - if (test_bit(HCI_ENCRYPT, &hdev->flags)) conn->link_mode |= HCI_LM_ENCRYPT; - /* Get remote features */ if (conn->type == ACL_LINK) { struct hci_cp_read_remote_features cp; @@ -915,20 +918,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s } else conn->state = BT_CLOSED; - if (conn->type == ACL_LINK) { - struct hci_conn *sco = conn->link; - if (sco) { - if (!ev->status) { - if (lmp_esco_capable(hdev)) - hci_setup_sync(sco, conn->handle); - else - hci_add_sco(sco, conn->handle); - } else { - hci_proto_connect_cfm(sco, ev->status); - hci_conn_del(sco); - } - } - } + if (conn->type == ACL_LINK) + hci_sco_setup(conn, ev->status); if (ev->status) { hci_proto_connect_cfm(conn, ev->status); @@ -938,7 +929,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s unlock: hci_dev_unlock(hdev); - hci_conn_check_pending(hdev); } @@ -1051,6 +1041,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (conn) { if (!ev->status) conn->link_mode |= HCI_LM_AUTH; + else + conn->sec_level = BT_SECURITY_LOW; clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); @@ -1322,7 +1314,7 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk if (ev->ncmd) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) - hci_sched_cmd(hdev); + tasklet_schedule(&hdev->cmd_task); } } @@ -1388,7 +1380,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->ncmd) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) - hci_sched_cmd(hdev); + tasklet_schedule(&hdev->cmd_task); } } @@ -1456,7 +1448,7 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s } } - hci_sched_tx(hdev); + tasklet_schedule(&hdev->tx_task); tasklet_enable(&hdev->tx_task); } @@ -1481,6 +1473,9 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb else conn->power_save = 0; } + + if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) + hci_sco_setup(conn, ev->status); } hci_dev_unlock(hdev); @@ -1701,8 +1696,9 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu break; case 0x10: /* Connection Accept Timeout */ + case 0x11: /* Unsupported Feature or Parameter Value */ case 0x1c: /* SCO interval rejected */ - case 0x1a: /* unsupported feature */ + case 0x1a: /* Unsupported Remote Feature */ case 0x1f: /* Unspecified error */ if (conn->out && conn->attempt < 2) { conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | @@ -1826,22 +1822,17 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; __u8 event = hdr->evt; - skb_pull(skb, HCI_EVENT_HDR_SIZE); - switch (event) { case HCI_EV_INQUIRY_COMPLETE: hci_inquiry_complete_evt(hdev, skb); break; - case HCI_EV_INQUIRY_RESULT: hci_inquiry_result_evt(hdev, skb); break; - case HCI_EV_CONN_COMPLETE: hci_conn_complete_evt(hdev, skb); break; - case HCI_EV_CONN_REQUEST: hci_conn_request_evt(hdev, skb); break; @@ -1957,12 +1948,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) case HCI_EV_REMOTE_HOST_FEATURES: hci_remote_host_features_evt(hdev, skb); break; - default: BT_DBG("%s event 0x%x", hdev->name, event); break; } - kfree_skb(skb); hdev->stat.evt_rx++; } @@ -1994,3 +1983,4 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) hci_send_to_sock(hdev, skb); kfree_skb(skb); } + diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index ef9aa4e8035..b4167e7992c 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -1,18 +1,15 @@ /* Bluetooth HCI driver model support. */ #include -#include #include -#include -#include #include #include -struct class *bt_class; +struct class *bt_class = NULL; +EXPORT_SYMBOL_GPL(bt_class); -struct dentry *bt_debugfs = NULL; -EXPORT_SYMBOL_GPL(bt_debugfs); +static struct workqueue_struct *bt_workq; static inline char *link_typetostr(int type) { @@ -159,14 +156,14 @@ void hci_conn_add_sysfs(struct hci_conn *conn) { BT_DBG("conn %p", conn); - queue_work(conn->hdev->workqueue, &conn->work_add); + queue_work(bt_workq, &conn->work_add); } void hci_conn_del_sysfs(struct hci_conn *conn) { BT_DBG("conn %p", conn); - queue_work(conn->hdev->workqueue, &conn->work_del); + queue_work(bt_workq, &conn->work_del); } static inline char *host_bustostr(int bus) @@ -191,6 +188,8 @@ static inline char *host_bustostr(int bus) } } + + static inline char *host_typetostr(int type) { switch (type) { @@ -203,18 +202,14 @@ static inline char *host_typetostr(int type) } } + + static ssize_t show_bus(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = dev_get_drvdata(dev); return sprintf(buf, "%s\n", host_bustostr(hdev->bus)); } -static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type)); -} - static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = dev_get_drvdata(dev); @@ -272,6 +267,32 @@ static ssize_t show_hci_revision(struct device *dev, struct device_attribute *at return sprintf(buf, "%d\n", hdev->hci_rev); } +static ssize_t show_inquiry_cache(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + struct inquiry_cache *cache = &hdev->inq_cache; + struct inquiry_entry *e; + int n = 0; + + hci_dev_lock_bh(hdev); + + for (e = cache->list; e; e = e->next) { + struct inquiry_data *data = &e->data; + bdaddr_t bdaddr; + baswap(&bdaddr, &data->bdaddr); + n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", + batostr(&bdaddr), + data->pscan_rep_mode, data->pscan_period_mode, + data->pscan_mode, data->dev_class[2], + data->dev_class[1], data->dev_class[0], + __le16_to_cpu(data->clock_offset), + data->rssi, data->ssp_mode, e->timestamp); + } + + hci_dev_unlock_bh(hdev); + return n; +} + static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = dev_get_drvdata(dev); @@ -281,9 +302,11 @@ static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *at static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct hci_dev *hdev = dev_get_drvdata(dev); - unsigned long val; + char *ptr; + __u32 val; - if (strict_strtoul(buf, 0, &val) < 0) + val = simple_strtoul(buf, &ptr, 10); + if (ptr == buf) return -EINVAL; if (val != 0 && (val < 500 || val > 3600000)) @@ -303,9 +326,11 @@ static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribu static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct hci_dev *hdev = dev_get_drvdata(dev); - unsigned long val; + char *ptr; + __u16 val; - if (strict_strtoul(buf, 0, &val) < 0) + val = simple_strtoul(buf, &ptr, 10); + if (ptr == buf) return -EINVAL; if (val < 0x0002 || val > 0xFFFE || val % 2) @@ -328,9 +353,11 @@ static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribu static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct hci_dev *hdev = dev_get_drvdata(dev); - unsigned long val; + char *ptr; + __u16 val; - if (strict_strtoul(buf, 0, &val) < 0) + val = simple_strtoul(buf, &ptr, 10); + if (ptr == buf) return -EINVAL; if (val < 0x0002 || val > 0xFFFE || val % 2) @@ -345,7 +372,6 @@ static ssize_t store_sniff_min_interval(struct device *dev, struct device_attrib } static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL); -static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(class, S_IRUGO, show_class, NULL); static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); @@ -353,6 +379,7 @@ static DEVICE_ATTR(features, S_IRUGO, show_features, NULL); static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL); static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL); static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL); +static DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL); static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR, show_idle_timeout, store_idle_timeout); @@ -363,7 +390,6 @@ static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, static struct attribute *bt_host_attrs[] = { &dev_attr_bus.attr, - &dev_attr_type.attr, &dev_attr_name.attr, &dev_attr_class.attr, &dev_attr_address.attr, @@ -371,6 +397,7 @@ static struct attribute *bt_host_attrs[] = { &dev_attr_manufacturer.attr, &dev_attr_hci_version.attr, &dev_attr_hci_revision.attr, + &dev_attr_inquiry_cache.attr, &dev_attr_idle_timeout.attr, &dev_attr_sniff_max_interval.attr, &dev_attr_sniff_min_interval.attr, @@ -398,44 +425,6 @@ static struct device_type bt_host = { .release = bt_host_release, }; -static int inquiry_cache_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct inquiry_cache *cache = &hdev->inq_cache; - struct inquiry_entry *e; - - hci_dev_lock_bh(hdev); - - for (e = cache->list; e; e = e->next) { - struct inquiry_data *data = &e->data; - bdaddr_t bdaddr; - baswap(&bdaddr, &data->bdaddr); - seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", - batostr(&bdaddr), - data->pscan_rep_mode, data->pscan_period_mode, - data->pscan_mode, data->dev_class[2], - data->dev_class[1], data->dev_class[0], - __le16_to_cpu(data->clock_offset), - data->rssi, data->ssp_mode, e->timestamp); - } - - hci_dev_unlock_bh(hdev); - - return 0; -} - -static int inquiry_cache_open(struct inode *inode, struct file *file) -{ - return single_open(file, inquiry_cache_show, inode->i_private); -} - -static const struct file_operations inquiry_cache_fops = { - .open = inquiry_cache_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - int hci_register_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; @@ -455,43 +444,34 @@ int hci_register_sysfs(struct hci_dev *hdev) if (err < 0) return err; - if (!bt_debugfs) - return 0; - - hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs); - if (!hdev->debugfs) - return 0; - - debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, - hdev, &inquiry_cache_fops); - return 0; } void hci_unregister_sysfs(struct hci_dev *hdev) { - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - debugfs_remove_recursive(hdev->debugfs); + BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->bus); device_del(&hdev->dev); } int __init bt_sysfs_init(void) { - bt_debugfs = debugfs_create_dir("bluetooth", NULL); + bt_workq = create_singlethread_workqueue("bluetooth"); + if (!bt_workq) + return -ENOMEM; bt_class = class_create(THIS_MODULE, "bluetooth"); - if (IS_ERR(bt_class)) + if (IS_ERR(bt_class)) { + destroy_workqueue(bt_workq); return PTR_ERR(bt_class); + } return 0; } void bt_sysfs_cleanup(void) { - class_destroy(bt_class); + destroy_workqueue(bt_workq); - debugfs_remove_recursive(bt_debugfs); + class_destroy(bt_class); } -