Commit 51c6d71d authored by Marcel Holtmann's avatar Marcel Holtmann

[Bluetooth] Initial sysfs and device class support

This patch adds the initial Bluetooth device class and sysfs support. This
new code replaces the hotplug and HCI proc interface.
parent 46e0ae91
......@@ -639,23 +639,23 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int
return err;
}
static int bfusb_probe(struct usb_interface *iface, const struct usb_device_id *id)
static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
const struct firmware *firmware;
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_endpoint *bulk_out_ep;
struct usb_host_endpoint *bulk_in_ep;
struct hci_dev *hdev;
struct bfusb *bfusb;
BT_DBG("iface %p id %p", iface, id);
BT_DBG("intf %p id %p", intf, id);
/* Check number of endpoints */
if (iface->altsetting[0].desc.bNumEndpoints < 2)
if (intf->altsetting[0].desc.bNumEndpoints < 2)
return -EIO;
bulk_out_ep = &iface->altsetting[0].endpoint[0];
bulk_in_ep = &iface->altsetting[0].endpoint[1];
bulk_out_ep = &intf->altsetting[0].endpoint[0];
bulk_in_ep = &intf->altsetting[0].endpoint[1];
if (!bulk_out_ep || !bulk_in_ep) {
BT_ERR("Bulk endpoints not found");
......@@ -702,6 +702,7 @@ static int bfusb_probe(struct usb_interface *iface, const struct usb_device_id *
hdev->type = HCI_USB;
hdev->driver_data = bfusb;
SET_HCIDEV_DEV(hdev, &intf->dev);
hdev->open = bfusb_open;
hdev->close = bfusb_close;
......@@ -717,7 +718,7 @@ static int bfusb_probe(struct usb_interface *iface, const struct usb_device_id *
goto error;
}
usb_set_intfdata(iface, bfusb);
usb_set_intfdata(intf, bfusb);
return 0;
......@@ -731,17 +732,17 @@ static int bfusb_probe(struct usb_interface *iface, const struct usb_device_id *
return -EIO;
}
static void bfusb_disconnect(struct usb_interface *iface)
static void bfusb_disconnect(struct usb_interface *intf)
{
struct bfusb *bfusb = usb_get_intfdata(iface);
struct bfusb *bfusb = usb_get_intfdata(intf);
struct hci_dev *hdev = &bfusb->hdev;
BT_DBG("iface %p", iface);
BT_DBG("intf %p", intf);
if (!hdev)
return;
usb_set_intfdata(iface, NULL);
usb_set_intfdata(intf, NULL);
bfusb_close(hdev);
......
......@@ -908,6 +908,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
hdev->type = HCI_USB;
hdev->driver_data = husb;
SET_HCIDEV_DEV(hdev, &intf->dev);
hdev->open = hci_usb_open;
hdev->close = hci_usb_close;
......
......@@ -119,6 +119,8 @@ struct hci_dev {
struct proc_dir_entry *proc;
#endif
struct class_device class_dev;
struct module *owner;
int (*open)(struct hci_dev *hdev);
......@@ -385,8 +387,10 @@ static inline int hci_recv_frame(struct sk_buff *skb)
return 0;
}
int hci_dev_proc_init(struct hci_dev *hdev);
void hci_dev_proc_cleanup(struct hci_dev *hdev);
int hci_register_sysfs(struct hci_dev *hdev);
void hci_unregister_sysfs(struct hci_dev *hdev);
#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->class_dev.dev = (pdev))
/* ----- LMP capabilities ----- */
#define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH)
......
......@@ -9,4 +9,4 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
obj-$(CONFIG_BT_CMTP) += cmtp/
bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_proc.o lib.o syms.o
bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o syms.o
......@@ -27,7 +27,7 @@
*
* $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $
*/
#define VERSION "2.3"
#define VERSION "2.4"
#include <linux/config.h>
#include <linux/module.h>
......@@ -331,8 +331,9 @@ struct net_proto_family bt_sock_family_ops = {
extern int hci_sock_init(void);
extern int hci_sock_cleanup(void);
extern int hci_proc_init(void);
extern int hci_proc_cleanup(void);
extern int bt_sysfs_init(void);
extern int bt_sysfs_cleanup(void);
static int __init bt_init(void)
{
......@@ -356,15 +357,18 @@ static int __init bt_init(void)
BT_INFO("HCI device and connection manager initialized");
hci_proc_init();
bt_sysfs_init();
hci_sock_init();
return 0;
}
static void __exit bt_cleanup(void)
{
hci_sock_cleanup();
hci_proc_cleanup();
bt_sysfs_cleanup();
sock_unregister(PF_BLUETOOTH);
kmem_cache_destroy(bt_sock_cache);
......@@ -375,7 +379,7 @@ static void __exit bt_cleanup(void)
subsys_initcall(bt_init);
module_exit(bt_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth Core ver " VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_BLUETOOTH);
......@@ -93,33 +93,6 @@ void hci_notify(struct hci_dev *hdev, int event)
notifier_call_chain(&hci_notifier, event, hdev);
}
/* ---- HCI hotplug support ---- */
#ifdef CONFIG_HOTPLUG
static int hci_run_hotplug(char *dev, char *action)
{
char *argv[3], *envp[5], dstr[20], astr[32];
sprintf(dstr, "DEVICE=%s", dev);
sprintf(astr, "ACTION=%s", action);
argv[0] = hotplug_path;
argv[1] = "bluetooth";
argv[2] = NULL;
envp[0] = "HOME=/";
envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
envp[2] = dstr;
envp[3] = astr;
envp[4] = NULL;
return call_usermodehelper(argv[0], argv, envp, 0);
}
#else
#define hci_run_hotplug(A...)
#endif
/* ---- HCI requests ---- */
void hci_req_complete(struct hci_dev *hdev, int result)
......@@ -841,10 +814,9 @@ int hci_register_dev(struct hci_dev *hdev)
write_unlock_bh(&hci_dev_list_lock);
hci_dev_proc_init(hdev);
hci_register_sysfs(hdev);
hci_notify(hdev, HCI_DEV_REG);
hci_run_hotplug(hdev->name, "register");
return id;
}
......@@ -854,7 +826,7 @@ int hci_unregister_dev(struct hci_dev *hdev)
{
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
hci_dev_proc_cleanup(hdev);
hci_unregister_sysfs(hdev);
write_lock_bh(&hci_dev_list_lock);
list_del(&hdev->list);
......@@ -863,7 +835,6 @@ int hci_unregister_dev(struct hci_dev *hdev)
hci_dev_do_close(hdev);
hci_notify(hdev, HCI_DEV_UNREG);
hci_run_hotplug(hdev->name, "unregister");
__hci_dev_put(hdev);
return 0;
......@@ -873,7 +844,6 @@ int hci_unregister_dev(struct hci_dev *hdev)
int hci_suspend_dev(struct hci_dev *hdev)
{
hci_notify(hdev, HCI_DEV_SUSPEND);
hci_run_hotplug(hdev->name, "suspend");
return 0;
}
......@@ -881,7 +851,6 @@ int hci_suspend_dev(struct hci_dev *hdev)
int hci_resume_dev(struct hci_dev *hdev)
{
hci_notify(hdev, HCI_DEV_RESUME);
hci_run_hotplug(hdev->name, "resume");
return 0;
}
......
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
/*
* Bluetooth HCI Proc FS support.
*
* $Id: hci_proc.c,v 1.0 2002/04/17 17:37:16 maxk Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/sock.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#ifndef CONFIG_BT_HCI_CORE_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_bt_hci;
static int hci_seq_open(struct file *file, struct seq_operations *op, void *priv)
{
struct seq_file *seq;
if (seq_open(file, op))
return -ENOMEM;
seq = file->private_data;
seq->private = priv;
return 0;
}
static void *inq_seq_start(struct seq_file *seq, loff_t *pos)
{
struct hci_dev *hdev = seq->private;
struct inquiry_entry *inq;
loff_t l = *pos;
hci_dev_lock_bh(hdev);
for (inq = hdev->inq_cache.list; inq; inq = inq->next)
if (!l--)
return inq;
return NULL;
}
static void *inq_seq_next(struct seq_file *seq, void *e, loff_t *pos)
{
struct inquiry_entry *inq = e;
return inq->next;
}
static void inq_seq_stop(struct seq_file *seq, void *e)
{
struct hci_dev *hdev = seq->private;
hci_dev_unlock_bh(hdev);
}
static int inq_seq_show(struct seq_file *seq, void *e)
{
struct inquiry_entry *inq = e;
struct inquiry_info *info = &inq->info;
seq_printf(seq, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %u\n", batostr(&info->bdaddr),
info->pscan_rep_mode, info->pscan_period_mode, info->pscan_mode,
info->dev_class[0], info->dev_class[1], info->dev_class[2],
info->clock_offset, inq->timestamp);
return 0;
}
static struct seq_operations inq_seq_ops = {
.start = inq_seq_start,
.next = inq_seq_next,
.stop = inq_seq_stop,
.show = inq_seq_show
};
static int inq_seq_open(struct inode *inode, struct file *file)
{
return hci_seq_open(file, &inq_seq_ops, PDE(inode)->data);
}
static struct file_operations inq_seq_fops = {
.owner = THIS_MODULE,
.open = inq_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
int hci_dev_proc_init(struct hci_dev *hdev)
{
struct proc_dir_entry *e;
char id[10];
sprintf(id, "%d", hdev->id);
hdev->proc = proc_mkdir(id, proc_bt_hci);
if (!hdev->proc)
return -ENOMEM;
e = create_proc_entry("inquiry_cache", S_IRUGO, hdev->proc);
if (e) {
e->proc_fops = &inq_seq_fops;
e->data = (void *) hdev;
}
return 0;
}
void hci_dev_proc_cleanup(struct hci_dev *hdev)
{
char id[10];
sprintf(id, "%d", hdev->id);
remove_proc_entry("inquiry_cache", hdev->proc);
remove_proc_entry(id, proc_bt_hci);
}
int __init hci_proc_init(void)
{
proc_bt_hci = proc_mkdir("hci", proc_bt);
return 0;
}
void __exit hci_proc_cleanup(void)
{
remove_proc_entry("hci", proc_bt);
}
#else /* CONFIG_PROC_FS */
int hci_dev_proc_init(struct hci_dev *hdev)
{
return 0;
}
void hci_dev_proc_cleanup(struct hci_dev *hdev)
{
return;
}
int __init hci_proc_init(void)
{
return 0;
}
void __exit hci_proc_cleanup(void)
{
return;
}
#endif /* CONFIG_PROC_FS */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#ifndef CONFIG_BT_HCI_CORE_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#endif
static ssize_t show_name(struct class_device *cdev, char *buf)
{
struct hci_dev *hdev = class_get_devdata(cdev);
return sprintf(buf, "%s\n", hdev->name);
}
static ssize_t show_type(struct class_device *cdev, char *buf)
{
struct hci_dev *hdev = class_get_devdata(cdev);
return sprintf(buf, "%d\n", hdev->type);
}
static ssize_t show_address(struct class_device *cdev, char *buf)
{
struct hci_dev *hdev = class_get_devdata(cdev);
bdaddr_t bdaddr;
baswap(&bdaddr, &hdev->bdaddr);
return sprintf(buf, "%s\n", batostr(&bdaddr));
}
static ssize_t show_flags(struct class_device *cdev, char *buf)
{
struct hci_dev *hdev = class_get_devdata(cdev);
return sprintf(buf, "0x%lx\n", hdev->flags);
}
static ssize_t show_inquiry_cache(struct class_device *cdev, char *buf)
{
struct hci_dev *hdev = class_get_devdata(cdev);
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_info *info = &e->info;
bdaddr_t bdaddr;
baswap(&bdaddr, &info->bdaddr);
n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x 0x%.2x %u\n",
batostr(&bdaddr),
info->pscan_rep_mode, info->pscan_period_mode, info->pscan_mode,
info->dev_class[0], info->dev_class[1], info->dev_class[2],
info->clock_offset, 0, e->timestamp);
}
hci_dev_unlock_bh(hdev);
return n;
}
static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static CLASS_DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
static CLASS_DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
static CLASS_DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL);
static struct class_device_attribute *bt_attrs[] = {
&class_device_attr_name,
&class_device_attr_type,
&class_device_attr_address,
&class_device_attr_flags,
&class_device_attr_inquiry_cache,
NULL
};
#ifdef CONFIG_HOTPLUG
static int bt_hotplug(struct class_device *cdev, char **envp, int num_envp, char *buf, int size)
{
struct hci_dev *hdev = class_get_devdata(cdev);
int n, i = 0;
envp[i++] = buf;
n = snprintf(buf, size, "INTERFACE=%s", hdev->name) + 1;
buf += n;
size -= n;
if ((size <= 0) || (i >= num_envp))
return -ENOMEM;
envp[i] = 0;
return 0;
}
#endif
static void bt_release(struct class_device *cdev)
{
}
static struct class bt_class = {
.name = "bluetooth",
.release = bt_release,
#ifdef CONFIG_HOTPLUG
.hotplug = bt_hotplug,
#endif
};
int hci_register_sysfs(struct hci_dev *hdev)
{
struct class_device *cdev = &hdev->class_dev;
int i, err;
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
cdev->class = &bt_class;
class_set_devdata(cdev, hdev);
strlcpy(cdev->class_id, hdev->name, BUS_ID_SIZE);
err = class_device_register(cdev);
if (err < 0)
return err;
for (i = 0; bt_attrs[i]; i++)
class_device_create_file(cdev, bt_attrs[i]);
return 0;
}
void hci_unregister_sysfs(struct hci_dev *hdev)
{
struct class_device * cdev = &hdev->class_dev;
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
class_device_del(cdev);
}
int __init bt_sysfs_init(void)
{
return class_register(&bt_class);
}
void __exit bt_sysfs_cleanup(void)
{
class_unregister(&bt_class);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment