Commit 4b283d59 authored by Maksim Krasnyanskiy's avatar Maksim Krasnyanskiy Committed by Marcel Holtmann

Improved support for /proc/bluetooth

- Convert /proc/bluetoth/l2cap to seq_file
- Convert /proc/bluetoth/rfcomm to seq_file
- Convert /proc/bluetooth/sco to seq_file
- Export HCI device info via /proc/bluetooth/hci/N
	 
parent da15d7b1
......@@ -63,6 +63,8 @@
#define BT_DMP(D...)
#endif
extern struct proc_dir_entry *proc_bt;
/* Connection and socket states */
enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
......
......@@ -29,6 +29,7 @@
#ifndef __HCI_CORE_H
#define __HCI_CORE_H
#include <linux/proc_fs.h>
#include <net/bluetooth/hci.h>
/* HCI upper protocols */
......@@ -37,6 +38,8 @@
#define HCI_INIT_TIMEOUT (HZ * 10)
extern struct proc_dir_entry *proc_bt_hci;
/* HCI Core structures */
struct inquiry_entry {
......@@ -111,6 +114,10 @@ struct hci_dev {
atomic_t promisc;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc;
#endif
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
......@@ -148,8 +155,8 @@ struct hci_conn {
};
extern struct hci_proto *hci_proto[];
extern struct list_head hdev_list;
extern rwlock_t hdev_list_lock;
extern struct list_head hci_dev_list;
extern rwlock_t hci_dev_list_lock;
/* ----- Inquiry cache ----- */
#define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds
......@@ -345,6 +352,9 @@ 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);
/* ----- LMP capabilities ----- */
#define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH)
#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT)
......
......@@ -345,4 +345,6 @@ int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg);
int rfcomm_init_ttys(void);
void rfcomm_cleanup_ttys(void);
extern struct proc_dir_entry *proc_bt_rfcomm;
#endif /* __RFCOMM_H */
......@@ -10,6 +10,6 @@ obj-$(CONFIG_BT_SCO) += sco.o
obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_proc.o lib.o syms.o
include $(TOPDIR)/Rules.make
......@@ -56,6 +56,10 @@
#define BT_DBG( A... )
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_bt;
#endif
/* Bluetooth sockets */
#define BT_MAX_PROTO 5
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
......@@ -323,12 +327,14 @@ 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);
static int __init bt_init(void)
{
BT_INFO("Core ver %s", VERSION);
proc_mkdir("bluetooth", NULL);
proc_bt = proc_mkdir("bluetooth", NULL);
/* Init socket cache */
bt_sock_cache = kmem_cache_create("bt_sock",
......@@ -344,6 +350,7 @@ static int __init bt_init(void)
BT_INFO("HCI device and connection manager initialized");
hci_proc_init();
hci_sock_init();
return 0;
}
......@@ -351,6 +358,7 @@ static int __init bt_init(void)
static void __exit bt_cleanup(void)
{
hci_sock_cleanup();
hci_proc_cleanup();
sock_unregister(PF_BLUETOOTH);
kmem_cache_destroy(bt_sock_cache);
......
......@@ -215,9 +215,9 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
BT_DBG("%s -> %s", batostr(src), batostr(dst));
read_lock_bh(&hdev_list_lock);
read_lock_bh(&hci_dev_list_lock);
list_for_each(p, &hdev_list) {
list_for_each(p, &hci_dev_list) {
struct hci_dev *d;
d = list_entry(p, struct hci_dev, list);
......@@ -243,7 +243,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
if (hdev)
hci_dev_hold(hdev);
read_unlock_bh(&hdev_list_lock);
read_unlock_bh(&hci_dev_list_lock);
return hdev;
}
......
......@@ -66,8 +66,8 @@ static void hci_notify(struct hci_dev *hdev, int event);
rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
/* HCI device list */
LIST_HEAD(hdev_list);
rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED;
LIST_HEAD(hci_dev_list);
rwlock_t hci_dev_list_lock = RW_LOCK_UNLOCKED;
/* HCI protocols */
#define HCI_MAX_PROTO 2
......@@ -298,8 +298,8 @@ struct hci_dev *hci_dev_get(int index)
if (index < 0)
return NULL;
read_lock(&hdev_list_lock);
list_for_each(p, &hdev_list) {
read_lock(&hci_dev_list_lock);
list_for_each(p, &hci_dev_list) {
hdev = list_entry(p, struct hci_dev, list);
if (hdev->id == index) {
hci_dev_hold(hdev);
......@@ -308,7 +308,7 @@ struct hci_dev *hci_dev_get(int index)
}
hdev = NULL;
done:
read_unlock(&hdev_list_lock);
read_unlock(&hci_dev_list_lock);
return hdev;
}
......@@ -727,8 +727,8 @@ int hci_get_dev_list(unsigned long arg)
return -ENOMEM;
dr = dl->dev_req;
read_lock_bh(&hdev_list_lock);
list_for_each(p, &hdev_list) {
read_lock_bh(&hci_dev_list_lock);
list_for_each(p, &hci_dev_list) {
struct hci_dev *hdev;
hdev = list_entry(p, struct hci_dev, list);
(dr + n)->dev_id = hdev->id;
......@@ -736,7 +736,7 @@ int hci_get_dev_list(unsigned long arg)
if (++n >= dev_num)
break;
}
read_unlock_bh(&hdev_list_lock);
read_unlock_bh(&hci_dev_list_lock);
dl->dev_num = n;
size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
......@@ -787,7 +787,7 @@ int hci_get_dev_info(unsigned long arg)
/* Register HCI device */
int hci_register_dev(struct hci_dev *hdev)
{
struct list_head *head = &hdev_list, *p;
struct list_head *head = &hci_dev_list, *p;
int id = 0;
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
......@@ -795,10 +795,10 @@ int hci_register_dev(struct hci_dev *hdev)
if (!hdev->open || !hdev->close || !hdev->destruct)
return -EINVAL;
write_lock_bh(&hdev_list_lock);
write_lock_bh(&hci_dev_list_lock);
/* Find first available device id */
list_for_each(p, &hdev_list) {
list_for_each(p, &hci_dev_list) {
if (list_entry(p, struct hci_dev, list)->id != id)
break;
head = p; id++;
......@@ -836,7 +836,9 @@ int hci_register_dev(struct hci_dev *hdev)
MOD_INC_USE_COUNT;
write_unlock_bh(&hdev_list_lock);
write_unlock_bh(&hci_dev_list_lock);
hci_dev_proc_init(hdev);
hci_notify(hdev, HCI_DEV_REG);
hci_run_hotplug(hdev->name, "register");
......@@ -849,9 +851,11 @@ int hci_unregister_dev(struct hci_dev *hdev)
{
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
write_lock_bh(&hdev_list_lock);
hci_dev_proc_cleanup(hdev);
write_lock_bh(&hci_dev_list_lock);
list_del(&hdev->list);
write_unlock_bh(&hdev_list_lock);
write_unlock_bh(&hci_dev_list_lock);
hci_dev_do_close(hdev);
......
/*
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 = {
.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 __init 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 __init hci_proc_cleanup(void)
{
return;
}
#endif /* CONFIG_PROC_FS */
......@@ -46,6 +46,7 @@
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <net/sock.h>
......@@ -1993,51 +1994,89 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
}
/* ---- Proc fs support ---- */
static int l2cap_sock_dump(char *buf, struct bt_sock_list *list)
#ifdef CONFIG_PROC_FS
static void *l2cap_seq_start(struct seq_file *seq, loff_t *pos)
{
struct l2cap_pinfo *pi;
struct sock *sk;
char *ptr = buf;
loff_t l = *pos;
read_lock_bh(&list->lock);
read_lock_bh(&l2cap_sk_list.lock);
for (sk = list->head; sk; sk = sk->next) {
pi = l2cap_pi(sk);
ptr += sprintf(ptr, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu,
pi->link_mode);
}
for (sk = l2cap_sk_list.head; sk; sk = sk->next)
if (!l--)
return sk;
return NULL;
}
read_unlock_bh(&list->lock);
static void *l2cap_seq_next(struct seq_file *seq, void *e, loff_t *pos)
{
struct sock *sk = e;
(*pos)++;
return sk->next;
}
ptr += sprintf(ptr, "\n");
return ptr - buf;
static void l2cap_seq_stop(struct seq_file *seq, void *e)
{
read_unlock_bh(&l2cap_sk_list.lock);
}
static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
static int l2cap_seq_show(struct seq_file *seq, void *e)
{
char *ptr = buf;
int len;
struct sock *sk = e;
struct l2cap_pinfo *pi = l2cap_pi(sk);
seq_printf(seq, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu,
pi->link_mode);
return 0;
}
static struct seq_operations l2cap_seq_ops = {
.start = l2cap_seq_start,
.next = l2cap_seq_next,
.stop = l2cap_seq_stop,
.show = l2cap_seq_show
};
BT_DBG("count %d, offset %ld", count, offset);
static int l2cap_seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &l2cap_seq_ops);
}
ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
len = ptr - buf;
static struct file_operations l2cap_seq_fops = {
.open = l2cap_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
if (len <= count + offset)
*eof = 1;
static int __init l2cap_proc_init(void)
{
struct proc_dir_entry *p = create_proc_entry("l2cap", S_IRUGO, proc_bt);
if (!p)
return -ENOMEM;
p->proc_fops = &l2cap_seq_fops;
return 0;
}
*start = buf + offset;
len -= offset;
static void __init l2cap_proc_cleanup(void)
{
remove_proc_entry("l2cap", proc_bt);
}
if (len > count)
len = count;
if (len < 0)
len = 0;
#else /* CONFIG_PROC_FS */
return len;
static int __init l2cap_proc_init(void)
{
return 0;
}
static void __init l2cap_proc_cleanup(void)
{
return 0;
}
#endif /* CONFIG_PROC_FS */
static struct proto_ops l2cap_sock_ops = {
.family = PF_BLUETOOTH,
......@@ -2088,8 +2127,9 @@ int __init l2cap_init(void)
return err;
}
create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
l2cap_proc_init();
BT_INFO("L2CAP ver %s", VERSION);
BT_INFO("L2CAP socket layer initialized");
......@@ -2098,7 +2138,7 @@ int __init l2cap_init(void)
void l2cap_cleanup(void)
{
remove_proc_entry("bluetooth/l2cap", NULL);
l2cap_proc_cleanup();
/* Unregister socket and protocol */
if (bt_sock_unregister(BTPROTO_L2CAP))
......
......@@ -43,6 +43,7 @@
#include <linux/wait.h>
#include <linux/net.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
......@@ -58,6 +59,10 @@
#define BT_DBG(D...)
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_bt_rfcomm;
#endif
struct task_struct *rfcomm_thread;
DECLARE_MUTEX(rfcomm_sem);
unsigned long rfcomm_event;
......@@ -1723,61 +1728,113 @@ static int rfcomm_run(void *unused)
}
/* ---- Proc fs support ---- */
static int rfcomm_dlc_dump(char *buf)
#ifdef CONFIG_PROC_FS
static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos)
{
struct rfcomm_session *s;
struct sock *sk;
struct list_head *p, *pp;
char *ptr = buf;
struct list_head *pp, *p;
loff_t l = *pos;
rfcomm_lock();
list_for_each(p, &session_list) {
s = list_entry(p, struct rfcomm_session, list);
sk = s->sock->sk;
list_for_each(pp, &s->dlcs)
if (!l--) {
seq->private = s;
return pp;
}
}
return NULL;
}
static void *rfcomm_seq_next(struct seq_file *seq, void *e, loff_t *pos)
{
struct rfcomm_session *s = seq->private;
struct list_head *pp, *p = e;
(*pos)++;
list_for_each(pp, &s->dlcs) {
struct rfcomm_dlc *d;
d = list_entry(pp, struct rfcomm_dlc, list);
if (p->next != &s->dlcs)
return p->next;
ptr += sprintf(ptr, "dlc %s %s %ld %d %d %d %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
for (p = s->list.next; p != &session_list; p = p->next) {
s = list_entry(p, struct rfcomm_session, list);
__list_for_each(pp, &s->dlcs) {
seq->private = s;
return pp;
}
}
return NULL;
}
static void rfcomm_seq_stop(struct seq_file *seq, void *e)
{
rfcomm_unlock();
}
static int rfcomm_seq_show(struct seq_file *seq, void *e)
{
struct rfcomm_session *s = seq->private;
struct sock *sk = s->sock->sk;
struct rfcomm_dlc *d = list_entry(e, struct rfcomm_dlc, list);
return ptr - buf;
seq_printf(seq, "%s %s %ld %d %d %d %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
return 0;
}
extern int rfcomm_sock_dump(char *buf);
static struct seq_operations rfcomm_seq_ops = {
.start = rfcomm_seq_start,
.next = rfcomm_seq_next,
.stop = rfcomm_seq_stop,
.show = rfcomm_seq_show
};
static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
static int rfcomm_seq_open(struct inode *inode, struct file *file)
{
char *ptr = buf;
int len;
return seq_open(file, &rfcomm_seq_ops);
}
BT_DBG("count %d, offset %ld", count, offset);
static struct file_operations rfcomm_seq_fops = {
.open = rfcomm_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
ptr += rfcomm_dlc_dump(ptr);
ptr += rfcomm_sock_dump(ptr);
len = ptr - buf;
static int __init rfcomm_proc_init(void)
{
struct proc_dir_entry *p;
if (len <= count + offset)
*eof = 1;
proc_bt_rfcomm = proc_mkdir("rfcomm", proc_bt);
*start = buf + offset;
len -= offset;
p = create_proc_entry("dlc", S_IRUGO, proc_bt_rfcomm);
if (p)
p->proc_fops = &rfcomm_seq_fops;
return 0;
}
if (len > count)
len = count;
if (len < 0)
len = 0;
static void __init rfcomm_proc_cleanup(void)
{
remove_proc_entry("dlc", proc_bt_rfcomm);
return len;
remove_proc_entry("rfcomm", proc_bt);
}
#else /* CONFIG_PROC_FS */
static int __init rfcomm_proc_init(void)
{
return 0;
}
static void __init rfcomm_proc_cleanup(void)
{
return 0;
}
#endif /* CONFIG_PROC_FS */
/* ---- Initialization ---- */
int __init rfcomm_init(void)
{
......@@ -1785,14 +1842,14 @@ int __init rfcomm_init(void)
BT_INFO("RFCOMM ver %s", VERSION);
rfcomm_proc_init();
rfcomm_init_sockets();
#ifdef CONFIG_BT_RFCOMM_TTY
rfcomm_init_ttys();
#endif
create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL);
return 0;
}
......@@ -1807,14 +1864,13 @@ void rfcomm_cleanup(void)
while (atomic_read(&running))
schedule();
remove_proc_entry("bluetooth/rfcomm", NULL);
#ifdef CONFIG_BT_RFCOMM_TTY
rfcomm_cleanup_ttys();
#endif
rfcomm_cleanup_sockets();
return;
rfcomm_proc_cleanup();
}
module_init(rfcomm_init);
......
......@@ -44,6 +44,8 @@
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/sock.h>
#include <asm/system.h>
......@@ -772,27 +774,87 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
}
/* ---- Proc fs support ---- */
int rfcomm_sock_dump(char *buf)
#ifdef CONFIG_PROC_FS
static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos)
{
struct bt_sock_list *list = &rfcomm_sk_list;
struct rfcomm_pinfo *pi;
struct sock *sk;
char *ptr = buf;
loff_t l = *pos;
write_lock_bh(&list->lock);
read_lock_bh(&rfcomm_sk_list.lock);
for (sk = list->head; sk; sk = sk->next) {
pi = rfcomm_pi(sk);
ptr += sprintf(ptr, "sk %s %s %d %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
sk->state, rfcomm_pi(sk)->channel);
}
for (sk = rfcomm_sk_list.head; sk; sk = sk->next)
if (!l--)
return sk;
return NULL;
}
static void *rfcomm_seq_next(struct seq_file *seq, void *e, loff_t *pos)
{
struct sock *sk = e;
(*pos)++;
return sk->next;
}
static void rfcomm_seq_stop(struct seq_file *seq, void *e)
{
read_unlock_bh(&rfcomm_sk_list.lock);
}
write_unlock_bh(&list->lock);
static int rfcomm_seq_show(struct seq_file *seq, void *e)
{
struct sock *sk = e;
seq_printf(seq, "%s %s %d %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
sk->state, rfcomm_pi(sk)->channel);
return 0;
}
static struct seq_operations rfcomm_seq_ops = {
.start = rfcomm_seq_start,
.next = rfcomm_seq_next,
.stop = rfcomm_seq_stop,
.show = rfcomm_seq_show
};
return ptr - buf;
static int rfcomm_seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &rfcomm_seq_ops);
}
static struct file_operations rfcomm_seq_fops = {
.open = rfcomm_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int __init rfcomm_sock_proc_init(void)
{
struct proc_dir_entry *p = create_proc_entry("sock", S_IRUGO, proc_bt_rfcomm);
if (!p)
return -ENOMEM;
p->proc_fops = &rfcomm_seq_fops;
return 0;
}
static void __init rfcomm_sock_proc_cleanup(void)
{
remove_proc_entry("sock", proc_bt_rfcomm);
}
#else /* CONFIG_PROC_FS */
static int __init rfcomm_sock_proc_init(void)
{
return 0;
}
static void __init rfcomm_sock_proc_cleanup(void)
{
return 0;
}
#endif /* CONFIG_PROC_FS */
static struct proto_ops rfcomm_sock_ops = {
.family = PF_BLUETOOTH,
.release = rfcomm_sock_release,
......@@ -826,6 +888,8 @@ int rfcomm_init_sockets(void)
return err;
}
rfcomm_sock_proc_init();
BT_INFO("RFCOMM socket layer initialized");
return 0;
}
......@@ -834,6 +898,8 @@ void rfcomm_cleanup_sockets(void)
{
int err;
rfcomm_sock_proc_cleanup();
/* Unregister socket, protocol and notifier */
if ((err = bt_sock_unregister(BTPROTO_RFCOMM)))
BT_ERR("RFCOMM socket layer unregistration failed. %d", err);
......
......@@ -46,6 +46,7 @@
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <net/sock.h>
......@@ -884,52 +885,86 @@ int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
return 0;
}
/* ----- Proc fs support ------ */
static int sco_sock_dump(char *buf, struct bt_sock_list *list)
/* ---- Proc fs support ---- */
#ifdef CONFIG_PROC_FS
static void *sco_seq_start(struct seq_file *seq, loff_t *pos)
{
struct sco_pinfo *pi;
struct sock *sk;
char *ptr = buf;
loff_t l = *pos;
read_lock_bh(&list->lock);
read_lock_bh(&sco_sk_list.lock);
for (sk = list->head; sk; sk = sk->next) {
pi = sco_pi(sk);
ptr += sprintf(ptr, "%s %s %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
sk->state);
}
for (sk = sco_sk_list.head; sk; sk = sk->next)
if (!l--)
return sk;
return NULL;
}
read_unlock_bh(&list->lock);
static void *sco_seq_next(struct seq_file *seq, void *e, loff_t *pos)
{
struct sock *sk = e;
(*pos)++;
return sk->next;
}
ptr += sprintf(ptr, "\n");
static void sco_seq_stop(struct seq_file *seq, void *e)
{
read_unlock_bh(&sco_sk_list.lock);
}
return ptr - buf;
static int sco_seq_show(struct seq_file *seq, void *e)
{
struct sock *sk = e;
seq_printf(seq, "%s %s %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->state);
return 0;
}
static int sco_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
static struct seq_operations sco_seq_ops = {
.start = sco_seq_start,
.next = sco_seq_next,
.stop = sco_seq_stop,
.show = sco_seq_show
};
static int sco_seq_open(struct inode *inode, struct file *file)
{
char *ptr = buf;
int len;
return seq_open(file, &sco_seq_ops);
}
BT_DBG("count %d, offset %ld", count, offset);
static struct file_operations sco_seq_fops = {
.open = sco_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
ptr += sco_sock_dump(ptr, &sco_sk_list);
len = ptr - buf;
static int __init sco_proc_init(void)
{
struct proc_dir_entry *p = create_proc_entry("sco", S_IRUGO, proc_bt);
if (!p)
return -ENOMEM;
p->proc_fops = &sco_seq_fops;
return 0;
}
if (len <= count + offset)
*eof = 1;
static void __init sco_proc_cleanup(void)
{
remove_proc_entry("sco", proc_bt);
}
*start = buf + offset;
len -= offset;
#else /* CONFIG_PROC_FS */
if (len > count)
len = count;
if (len < 0)
len = 0;
static int __init sco_proc_init(void)
{
return 0;
}
return len;
static void __init sco_proc_cleanup(void)
{
return 0;
}
#endif /* CONFIG_PROC_FS */
static struct proto_ops sco_sock_ops = {
.family = PF_BLUETOOTH,
......@@ -978,8 +1013,8 @@ int __init sco_init(void)
return err;
}
create_proc_read_entry("bluetooth/sco", 0, 0, sco_read_proc, NULL);
sco_proc_init();
BT_INFO("SCO (Voice Link) ver %s", VERSION);
BT_INFO("SCO socket layer initialized");
......@@ -990,7 +1025,7 @@ void sco_cleanup(void)
{
int err;
remove_proc_entry("bluetooth/sco", NULL);
sco_proc_cleanup();
/* Unregister socket, protocol and notifier */
if ((err = bt_sock_unregister(BTPROTO_SCO)))
......
......@@ -78,3 +78,5 @@ EXPORT_SYMBOL(bt_sock_poll);
EXPORT_SYMBOL(bt_accept_enqueue);
EXPORT_SYMBOL(bt_accept_dequeue);
EXPORT_SYMBOL(bt_sock_w4_connect);
EXPORT_SYMBOL(proc_bt);
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