OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/linux/linux-2.4/net/bluetooth/cmtp
    from Rev 1275 to Rev 1765
    Reverse comparison

Rev 1275 → Rev 1765

/sock.c
0,0 → 1,208
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
 
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.
*/
 
#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/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <net/sock.h>
 
#include <asm/system.h>
#include <asm/uaccess.h>
 
#include "cmtp.h"
 
#ifndef CONFIG_BLUEZ_CMTP_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
 
static int cmtp_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
 
BT_DBG("sock %p sk %p", sock, sk);
 
if (!sk)
return 0;
 
sock_orphan(sk);
sock_put(sk);
 
MOD_DEC_USE_COUNT;
return 0;
}
 
static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct cmtp_connadd_req ca;
struct cmtp_conndel_req cd;
struct cmtp_connlist_req cl;
struct cmtp_conninfo ci;
struct socket *nsock;
int err;
 
BT_DBG("cmd %x arg %lx", cmd, arg);
 
switch (cmd) {
case CMTPCONNADD:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
 
if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
return -EFAULT;
 
nsock = sockfd_lookup(ca.sock, &err);
if (!nsock)
return err;
 
if (nsock->sk->state != BT_CONNECTED) {
fput(nsock->file);
return -EBADFD;
}
 
err = cmtp_add_connection(&ca, nsock);
if (!err) {
if (copy_to_user((void *) arg, &ca, sizeof(ca)))
err = -EFAULT;
} else
fput(nsock->file);
 
return err;
 
case CMTPCONNDEL:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
 
if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
return -EFAULT;
 
return cmtp_del_connection(&cd);
 
case CMTPGETCONNLIST:
if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
return -EFAULT;
 
if (cl.cnum <= 0)
return -EINVAL;
 
err = cmtp_get_connlist(&cl);
if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
return -EFAULT;
 
return err;
 
case CMTPGETCONNINFO:
if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
return -EFAULT;
 
err = cmtp_get_conninfo(&ci);
if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
return -EFAULT;
 
return err;
}
 
return -EINVAL;
}
 
static struct proto_ops cmtp_sock_ops = {
family: PF_BLUETOOTH,
release: cmtp_sock_release,
ioctl: cmtp_sock_ioctl,
bind: sock_no_bind,
getname: sock_no_getname,
sendmsg: sock_no_sendmsg,
recvmsg: sock_no_recvmsg,
poll: sock_no_poll,
listen: sock_no_listen,
shutdown: sock_no_shutdown,
setsockopt: sock_no_setsockopt,
getsockopt: sock_no_getsockopt,
connect: sock_no_connect,
socketpair: sock_no_socketpair,
accept: sock_no_accept,
mmap: sock_no_mmap
};
 
static int cmtp_sock_create(struct socket *sock, int protocol)
{
struct sock *sk;
 
BT_DBG("sock %p", sock);
 
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
 
sock->ops = &cmtp_sock_ops;
 
if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
return -ENOMEM;
 
MOD_INC_USE_COUNT;
 
sock->state = SS_UNCONNECTED;
sock_init_data(sock, sk);
 
sk->destruct = NULL;
sk->protocol = protocol;
 
return 0;
}
 
static struct net_proto_family cmtp_sock_family_ops = {
family: PF_BLUETOOTH,
create: cmtp_sock_create
};
 
int cmtp_init_sockets(void)
{
int err;
 
if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) {
BT_ERR("Can't register CMTP socket layer (%d)", err);
return err;
}
 
return 0;
}
 
void cmtp_cleanup_sockets(void)
{
int err;
 
if ((err = bluez_sock_unregister(BTPROTO_CMTP)))
BT_ERR("Can't unregister CMTP socket layer (%d)", err);
 
return;
}
/cmtp.h
0,0 → 1,138
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
 
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.
*/
 
#ifndef __CMTP_H
#define __CMTP_H
 
#include <linux/types.h>
#include <net/bluetooth/bluetooth.h>
 
#define BTNAMSIZ 18
 
/* CMTP ioctl defines */
#define CMTPCONNADD _IOW('C', 200, int)
#define CMTPCONNDEL _IOW('C', 201, int)
#define CMTPGETCONNLIST _IOR('C', 210, int)
#define CMTPGETCONNINFO _IOR('C', 211, int)
 
#define CMTP_LOOPBACK 0
 
struct cmtp_connadd_req {
int sock; // Connected socket
__u32 flags;
};
 
struct cmtp_conndel_req {
bdaddr_t bdaddr;
__u32 flags;
};
 
struct cmtp_conninfo {
bdaddr_t bdaddr;
__u32 flags;
__u16 state;
int num;
};
 
struct cmtp_connlist_req {
__u32 cnum;
struct cmtp_conninfo *ci;
};
 
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
int cmtp_del_connection(struct cmtp_conndel_req *req);
int cmtp_get_connlist(struct cmtp_connlist_req *req);
int cmtp_get_conninfo(struct cmtp_conninfo *ci);
 
/* CMTP session defines */
#define CMTP_INTEROP_TIMEOUT (HZ * 5)
#define CMTP_INITIAL_MSGNUM 0xff00
 
struct cmtp_session {
struct list_head list;
 
struct socket *sock;
 
bdaddr_t bdaddr;
 
unsigned long state;
unsigned long flags;
 
uint mtu;
 
char name[BTNAMSIZ];
 
atomic_t terminate;
 
wait_queue_head_t wait;
 
int ncontroller;
int num;
struct capi_ctr *ctrl;
 
struct list_head applications;
 
unsigned long blockids;
int msgnum;
 
struct sk_buff_head transmit;
 
struct sk_buff *reassembly[16];
};
 
struct cmtp_application {
struct list_head list;
 
unsigned long state;
int err;
 
__u16 appl;
__u16 mapping;
 
__u16 msgnum;
};
 
struct cmtp_scb {
int id;
int data;
};
 
int cmtp_attach_device(struct cmtp_session *session);
void cmtp_detach_device(struct cmtp_session *session);
 
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
 
static inline void cmtp_schedule(struct cmtp_session *session)
{
struct sock *sk = session->sock->sk;
 
wake_up_interruptible(sk->sleep);
}
 
/* CMTP init defines */
int cmtp_init_capi(void);
int cmtp_init_sockets(void);
void cmtp_cleanup_capi(void);
void cmtp_cleanup_sockets(void);
 
#endif /* __CMTP_H */
/core.c
0,0 → 1,515
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
 
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.
*/
 
#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/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/init.h>
#include <net/sock.h>
 
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
 
#include "cmtp.h"
 
#ifndef CONFIG_BLUEZ_CMTP_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
 
#define VERSION "1.0"
 
static DECLARE_RWSEM(cmtp_session_sem);
static LIST_HEAD(cmtp_session_list);
 
static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
{
struct cmtp_session *session;
struct list_head *p;
 
BT_DBG("");
 
list_for_each(p, &cmtp_session_list) {
session = list_entry(p, struct cmtp_session, list);
if (!bacmp(bdaddr, &session->bdaddr))
return session;
}
return NULL;
}
 
static void __cmtp_link_session(struct cmtp_session *session)
{
MOD_INC_USE_COUNT;
list_add(&session->list, &cmtp_session_list);
}
 
static void __cmtp_unlink_session(struct cmtp_session *session)
{
list_del(&session->list);
MOD_DEC_USE_COUNT;
}
 
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
{
bacpy(&ci->bdaddr, &session->bdaddr);
 
ci->flags = session->flags;
ci->state = session->state;
 
ci->num = session->num;
}
 
 
static inline int cmtp_alloc_block_id(struct cmtp_session *session)
{
int i, id = -1;
 
for (i = 0; i < 16; i++)
if (!test_and_set_bit(i, &session->blockids)) {
id = i;
break;
}
 
return id;
}
 
static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
{
clear_bit(id, &session->blockids);
}
 
static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
{
struct sk_buff *skb = session->reassembly[id], *nskb;
int size;
 
BT_DBG("session %p buf %p count %d", session, buf, count);
 
size = (skb) ? skb->len + count : count;
 
if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
BT_ERR("Can't allocate memory for CAPI message");
return;
}
 
if (skb && (skb->len > 0))
memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
 
memcpy(skb_put(nskb, count), buf, count);
 
session->reassembly[id] = nskb;
 
if (skb)
kfree_skb(skb);
}
 
static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
{
__u8 hdr, hdrlen, id;
__u16 len;
 
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 
while (skb->len > 0) {
hdr = skb->data[0];
 
switch (hdr & 0xc0) {
case 0x40:
hdrlen = 2;
len = skb->data[1];
break;
case 0x80:
hdrlen = 3;
len = skb->data[1] | (skb->data[2] << 8);
break;
default:
hdrlen = 1;
len = 0;
break;
}
 
id = (hdr & 0x3c) >> 2;
 
BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
 
if (hdrlen + len > skb->len) {
BT_ERR("Wrong size or header information in CMTP frame");
break;
}
 
if (len == 0) {
skb_pull(skb, hdrlen);
continue;
}
 
switch (hdr & 0x03) {
case 0x00:
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
cmtp_recv_capimsg(session, session->reassembly[id]);
session->reassembly[id] = NULL;
break;
case 0x01:
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
break;
default:
if (session->reassembly[id] != NULL)
kfree_skb(session->reassembly[id]);
session->reassembly[id] = NULL;
break;
}
 
skb_pull(skb, hdrlen + len);
}
 
kfree_skb(skb);
return 0;
}
 
static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
{
struct socket *sock = session->sock;
struct iovec iv = { data, len };
struct msghdr msg;
int err;
 
BT_DBG("session %p data %p len %d", session, data, len);
 
if (!len)
return 0;
 
memset(&msg, 0, sizeof(msg));
msg.msg_iovlen = 1;
msg.msg_iov = &iv;
 
err = sock->ops->sendmsg(sock, &msg, len, 0);
return err;
}
 
static int cmtp_process_transmit(struct cmtp_session *session)
{
struct sk_buff *skb, *nskb;
unsigned char *hdr;
unsigned int size, tail;
 
BT_DBG("session %p", session);
 
if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
BT_ERR("Can't allocate memory for new frame");
return -ENOMEM;
}
 
while ((skb = skb_dequeue(&session->transmit))) {
struct cmtp_scb *scb = (void *) skb->cb;
 
if ((tail = (session->mtu - nskb->len)) < 5) {
cmtp_send_frame(session, nskb->data, nskb->len);
skb_trim(nskb, 0);
tail = session->mtu;
}
 
size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
 
if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
skb_queue_head(&session->transmit, skb);
break;
}
 
if (size < 256) {
hdr = skb_put(nskb, 2);
hdr[0] = 0x40
| ((scb->id << 2) & 0x3c)
| ((skb->len == size) ? 0x00 : 0x01);
hdr[1] = size;
} else {
hdr = skb_put(nskb, 3);
hdr[0] = 0x80
| ((scb->id << 2) & 0x3c)
| ((skb->len == size) ? 0x00 : 0x01);
hdr[1] = size & 0xff;
hdr[2] = size >> 8;
}
 
memcpy(skb_put(nskb, size), skb->data, size);
skb_pull(skb, size);
 
if (skb->len > 0) {
skb_queue_head(&session->transmit, skb);
} else {
cmtp_free_block_id(session, scb->id);
if (scb->data) {
cmtp_send_frame(session, nskb->data, nskb->len);
skb_trim(nskb, 0);
}
kfree_skb(skb);
}
}
 
cmtp_send_frame(session, nskb->data, nskb->len);
 
kfree_skb(nskb);
 
return skb_queue_len(&session->transmit);
}
 
static int cmtp_session(void *arg)
{
struct cmtp_session *session = arg;
struct sock *sk = session->sock->sk;
struct sk_buff *skb;
wait_queue_t wait;
 
BT_DBG("session %p", session);
 
daemonize(); reparent_to_init();
 
sprintf(current->comm, "kcmtpd_ctr_%d", session->num);
 
sigfillset(&current->blocked);
flush_signals(current);
 
current->nice = -15;
 
set_fs(KERNEL_DS);
 
init_waitqueue_entry(&wait, current);
add_wait_queue(sk->sleep, &wait);
while (!atomic_read(&session->terminate)) {
set_current_state(TASK_INTERRUPTIBLE);
 
if (sk->state != BT_CONNECTED)
break;
 
while ((skb = skb_dequeue(&sk->receive_queue))) {
skb_orphan(skb);
cmtp_recv_frame(session, skb);
}
 
cmtp_process_transmit(session);
 
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
 
down_write(&cmtp_session_sem);
 
if (!(session->flags & (1 << CMTP_LOOPBACK)))
cmtp_detach_device(session);
 
fput(session->sock->file);
 
__cmtp_unlink_session(session);
 
up_write(&cmtp_session_sem);
 
kfree(session);
return 0;
}
 
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
{
struct cmtp_session *session, *s;
bdaddr_t src, dst;
int i, err;
 
BT_DBG("");
 
baswap(&src, &bluez_pi(sock->sk)->src);
baswap(&dst, &bluez_pi(sock->sk)->dst);
 
session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
if (!session)
return -ENOMEM;
memset(session, 0, sizeof(struct cmtp_session));
 
down_write(&cmtp_session_sem);
 
s = __cmtp_get_session(&bluez_pi(sock->sk)->dst);
if (s && s->state == BT_CONNECTED) {
err = -EEXIST;
goto failed;
}
 
bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst);
 
session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
 
BT_DBG("mtu %d", session->mtu);
 
sprintf(session->name, "%s", batostr(&dst));
 
session->sock = sock;
session->state = BT_CONFIG;
 
init_waitqueue_head(&session->wait);
 
session->ctrl = NULL;
session->msgnum = CMTP_INITIAL_MSGNUM;
 
INIT_LIST_HEAD(&session->applications);
 
skb_queue_head_init(&session->transmit);
 
for (i = 0; i < 16; i++)
session->reassembly[i] = NULL;
 
session->flags = req->flags;
 
__cmtp_link_session(session);
 
err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
if (err < 0)
goto unlink;
 
if (!(session->flags & (1 << CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
if (err < 0)
goto detach;
}
 
up_write(&cmtp_session_sem);
return 0;
 
detach:
cmtp_detach_device(session);
 
unlink:
__cmtp_unlink_session(session);
 
failed:
up_write(&cmtp_session_sem);
kfree(session);
return err;
}
 
int cmtp_del_connection(struct cmtp_conndel_req *req)
{
struct cmtp_session *session;
int err = 0;
 
BT_DBG("");
 
down_read(&cmtp_session_sem);
 
session = __cmtp_get_session(&req->bdaddr);
if (session) {
/* Flush the transmit queue */
skb_queue_purge(&session->transmit);
 
/* Kill session thread */
atomic_inc(&session->terminate);
cmtp_schedule(session);
} else
err = -ENOENT;
 
up_read(&cmtp_session_sem);
return err;
}
 
int cmtp_get_connlist(struct cmtp_connlist_req *req)
{
struct list_head *p;
int err = 0, n = 0;
 
BT_DBG("");
 
down_read(&cmtp_session_sem);
 
list_for_each(p, &cmtp_session_list) {
struct cmtp_session *session;
struct cmtp_conninfo ci;
 
session = list_entry(p, struct cmtp_session, list);
 
__cmtp_copy_session(session, &ci);
 
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
err = -EFAULT;
break;
}
 
if (++n >= req->cnum)
break;
 
req->ci++;
}
req->cnum = n;
 
up_read(&cmtp_session_sem);
return err;
}
 
int cmtp_get_conninfo(struct cmtp_conninfo *ci)
{
struct cmtp_session *session;
int err = 0;
 
down_read(&cmtp_session_sem);
 
session = __cmtp_get_session(&ci->bdaddr);
if (session)
__cmtp_copy_session(session, ci);
else
err = -ENOENT;
 
up_read(&cmtp_session_sem);
return err;
}
 
 
int __init init_cmtp(void)
{
l2cap_load();
 
cmtp_init_capi();
cmtp_init_sockets();
 
BT_INFO("BlueZ CMTP ver %s", VERSION);
BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>");
 
return 0;
}
 
void __exit exit_cmtp(void)
{
cmtp_cleanup_sockets();
cmtp_cleanup_capi();
}
 
module_init(init_cmtp);
module_exit(exit_cmtp);
 
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION);
MODULE_LICENSE("GPL");
/Config.in
0,0 → 1,7
#
# Bluetooth CMTP layer configuration
#
 
if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then
dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP
fi
/Makefile
0,0 → 1,10
#
# Makefile for the Linux Bluetooth CMTP layer
#
 
O_TARGET := cmtp.o
 
obj-y := core.o sock.o capi.o
obj-m += $(O_TARGET)
 
include $(TOPDIR)/Rules.make
/capi.c
0,0 → 1,707
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
 
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.
*/
 
#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/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <net/sock.h>
 
#include <linux/capi.h>
 
#include "../drivers/isdn/avmb1/capilli.h"
#include "../drivers/isdn/avmb1/capicmd.h"
#include "../drivers/isdn/avmb1/capiutil.h"
 
#include "cmtp.h"
 
#ifndef CONFIG_BLUEZ_CMTP_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
 
#define REVISION "1.0"
 
#define CAPI_INTEROPERABILITY 0x20
 
#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
 
#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
 
#define CAPI_FUNCTION_REGISTER 0
#define CAPI_FUNCTION_RELEASE 1
#define CAPI_FUNCTION_GET_PROFILE 2
#define CAPI_FUNCTION_GET_MANUFACTURER 3
#define CAPI_FUNCTION_GET_VERSION 4
#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
#define CAPI_FUNCTION_MANUFACTURER 6
#define CAPI_FUNCTION_LOOPBACK 7
 
static struct capi_driver_interface *di;
 
 
#define CMTP_MSGNUM 1
#define CMTP_APPLID 2
#define CMTP_MAPPING 3
 
static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
{
struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
 
BT_DBG("session %p application %p appl %d", session, app, appl);
 
if (!app)
return NULL;
 
memset(app, 0, sizeof(*app));
 
app->state = BT_OPEN;
app->appl = appl;
 
list_add_tail(&app->list, &session->applications);
 
return app;
}
 
static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
{
BT_DBG("session %p application %p", session, app);
 
if (app) {
list_del(&app->list);
kfree(app);
}
}
 
static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
{
struct cmtp_application *app;
struct list_head *p, *n;
 
list_for_each_safe(p, n, &session->applications) {
app = list_entry(p, struct cmtp_application, list);
switch (pattern) {
case CMTP_MSGNUM:
if (app->msgnum == value)
return app;
break;
case CMTP_APPLID:
if (app->appl == value)
return app;
break;
case CMTP_MAPPING:
if (app->mapping == value)
return app;
break;
}
}
 
return NULL;
}
 
static int cmtp_msgnum_get(struct cmtp_session *session)
{
session->msgnum++;
 
if ((session->msgnum & 0xff) > 200)
session->msgnum = CMTP_INITIAL_MSGNUM + 1;
 
return session->msgnum;
}
 
 
static void cmtp_send_interopmsg(struct cmtp_session *session,
__u8 subcmd, __u16 appl, __u16 msgnum,
__u16 function, unsigned char *buf, int len)
{
struct sk_buff *skb;
unsigned char *s;
 
BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
 
if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
BT_ERR("Can't allocate memory for interoperability packet");
return;
}
 
s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
 
capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
capimsg_setu16(s, 2, appl);
capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
capimsg_setu8 (s, 5, subcmd);
capimsg_setu16(s, 6, msgnum);
 
/* Interoperability selector (Bluetooth Device Management) */
capimsg_setu16(s, 8, 0x0001);
 
capimsg_setu8 (s, 10, 3 + len);
capimsg_setu16(s, 11, function);
capimsg_setu8 (s, 13, len);
 
if (len > 0)
memcpy(s + 14, buf, len);
 
cmtp_send_capimsg(session, skb);
}
 
static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = session->ctrl;
struct cmtp_application *application;
__u16 appl, msgnum, func, info;
__u32 controller;
 
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 
switch (CAPIMSG_SUBCOMMAND(skb->data)) {
case CAPI_CONF:
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
 
switch (func) {
case CAPI_FUNCTION_REGISTER:
msgnum = CAPIMSG_MSGID(skb->data);
 
application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
if (application) {
application->state = BT_CONNECTED;
application->msgnum = 0;
application->mapping = CAPIMSG_APPID(skb->data);
wake_up_interruptible(&session->wait);
}
 
break;
 
case CAPI_FUNCTION_RELEASE:
appl = CAPIMSG_APPID(skb->data);
 
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
application->state = BT_CLOSED;
application->msgnum = 0;
wake_up_interruptible(&session->wait);
}
 
break;
 
case CAPI_FUNCTION_GET_PROFILE:
controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
msgnum = CAPIMSG_MSGID(skb->data);
 
if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
session->ncontroller = controller;
wake_up_interruptible(&session->wait);
break;
}
 
if (!info && ctrl) {
memcpy(&ctrl->profile,
skb->data + CAPI_MSG_BASELEN + 11,
sizeof(capi_profile));
session->state = BT_CONNECTED;
ctrl->ready(ctrl);
}
 
break;
 
case CAPI_FUNCTION_GET_MANUFACTURER:
controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
 
if (!info && ctrl) {
strncpy(ctrl->manu,
skb->data + CAPI_MSG_BASELEN + 15,
skb->data[CAPI_MSG_BASELEN + 14]);
}
 
break;
 
case CAPI_FUNCTION_GET_VERSION:
controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
 
if (!info && ctrl) {
ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
}
 
break;
 
case CAPI_FUNCTION_GET_SERIAL_NUMBER:
controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
 
if (!info && ctrl) {
memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
strncpy(ctrl->serial,
skb->data + CAPI_MSG_BASELEN + 17,
skb->data[CAPI_MSG_BASELEN + 16]);
}
 
break;
}
 
break;
 
case CAPI_IND:
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
 
if (func == CAPI_FUNCTION_LOOPBACK) {
appl = CAPIMSG_APPID(skb->data);
msgnum = CAPIMSG_MSGID(skb->data);
cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
skb->data + CAPI_MSG_BASELEN + 6,
skb->data[CAPI_MSG_BASELEN + 5]);
}
 
break;
}
 
kfree_skb(skb);
}
 
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = session->ctrl;
struct cmtp_application *application;
__u16 cmd, appl, info;
__u32 ncci, contr;
 
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 
if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
cmtp_recv_interopmsg(session, skb);
return;
}
 
if (session->flags & (1 << CMTP_LOOPBACK)) {
kfree_skb(skb);
return;
}
 
cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
 
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
appl = application->appl;
CAPIMSG_SETAPPID(skb->data, appl);
} else {
BT_ERR("Can't find application with id %d", appl);
kfree_skb(skb);
return;
}
 
if ((contr & 0x7f) == 0x01) {
contr = (contr & 0xffffff80) | session->num;
CAPIMSG_SETCONTROL(skb->data, contr);
}
 
if (!ctrl) {
BT_ERR("Can't find controller %d for message", session->num);
kfree_skb(skb);
return;
}
 
switch (cmd) {
case CAPI_CONNECT_B3_CONF:
ncci = CAPIMSG_NCCI(skb->data);
info = CAPIMSG_U16(skb->data, 12);
 
BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info);
 
if (info == 0)
ctrl->new_ncci(ctrl, appl, ncci, 8);
 
ctrl->handle_capimsg(ctrl, appl, skb);
break;
 
case CAPI_CONNECT_B3_IND:
ncci = CAPIMSG_NCCI(skb->data);
 
BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci);
 
ctrl->new_ncci(ctrl, appl, ncci, 8);
ctrl->handle_capimsg(ctrl, appl, skb);
break;
 
case CAPI_DISCONNECT_B3_IND:
ncci = CAPIMSG_NCCI(skb->data);
 
BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci);
 
if (ncci == 0xffffffff)
BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff");
 
ctrl->handle_capimsg(ctrl, appl, skb);
ctrl->free_ncci(ctrl, appl, ncci);
break;
 
default:
ctrl->handle_capimsg(ctrl, appl, skb);
break;
}
}
 
void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct cmtp_scb *scb = (void *) skb->cb;
 
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 
scb->id = -1;
scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
 
skb_queue_tail(&session->transmit, skb);
 
cmtp_schedule(session);
}
 
 
static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
{
BT_DBG("ctrl %p data %p", ctrl, data);
 
return -EIO;
}
 
static void cmtp_reset_ctr(struct capi_ctr *ctrl)
{
BT_DBG("ctrl %p", ctrl);
 
ctrl->reseted(ctrl);
}
 
static void cmtp_remove_ctr(struct capi_ctr *ctrl)
{
struct cmtp_session *session = ctrl->driverdata;
 
BT_DBG("ctrl %p", ctrl);
 
ctrl->suspend_output(ctrl);
 
atomic_inc(&session->terminate);
cmtp_schedule(session);
}
 
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
{
DECLARE_WAITQUEUE(wait, current);
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
unsigned long timeo = CMTP_INTEROP_TIMEOUT;
unsigned char buf[8];
int err = 0, nconn, want = rp->level3cnt;
 
BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
 
application = cmtp_application_add(session, appl);
if (!application) {
BT_ERR("Can't allocate memory for new application");
ctrl->appl_released(ctrl, appl);
return;
}
 
if (want < 0)
nconn = ctrl->profile.nbchannel * -want;
else
nconn = want;
 
if (nconn == 0)
nconn = ctrl->profile.nbchannel;
 
capimsg_setu16(buf, 0, nconn);
capimsg_setu16(buf, 2, rp->datablkcnt);
capimsg_setu16(buf, 4, rp->datablklen);
 
application->state = BT_CONFIG;
application->msgnum = cmtp_msgnum_get(session);
 
cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
CAPI_FUNCTION_REGISTER, buf, 6);
 
add_wait_queue(&session->wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
 
if (!timeo) {
err = -EAGAIN;
break;
}
 
if (application->state == BT_CLOSED) {
err = -application->err;
break;
}
 
if (application->state == BT_CONNECTED)
break;
 
if (signal_pending(current)) {
err = -EINTR;
break;
}
 
timeo = schedule_timeout(timeo);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&session->wait, &wait);
 
if (err) {
ctrl->appl_released(ctrl, appl);
cmtp_application_del(session, application);
return;
}
 
ctrl->appl_registered(ctrl, appl);
}
 
static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
{
DECLARE_WAITQUEUE(wait, current);
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
unsigned long timeo = CMTP_INTEROP_TIMEOUT;
 
BT_DBG("ctrl %p appl %d", ctrl, appl);
 
application = cmtp_application_get(session, CMTP_APPLID, appl);
if (!application) {
BT_ERR("Can't find application");
return;
}
 
application->msgnum = cmtp_msgnum_get(session);
 
cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
CAPI_FUNCTION_RELEASE, NULL, 0);
 
add_wait_queue(&session->wait, &wait);
while (timeo) {
set_current_state(TASK_INTERRUPTIBLE);
 
if (application->state == BT_CLOSED)
break;
 
if (signal_pending(current))
break;
 
timeo = schedule_timeout(timeo);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&session->wait, &wait);
 
cmtp_application_del(session, application);
ctrl->appl_released(ctrl, appl);
}
 
static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
__u16 appl;
__u32 contr;
 
BT_DBG("ctrl %p skb %p", ctrl, skb);
 
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
 
application = cmtp_application_get(session, CMTP_APPLID, appl);
if ((!application) || (application->state != BT_CONNECTED)) {
BT_ERR("Can't find application with id %d", appl);
kfree_skb(skb);
return;
}
 
CAPIMSG_SETAPPID(skb->data, application->mapping);
 
if ((contr & 0x7f) == session->num) {
contr = (contr & 0xffffff80) | 0x01;
CAPIMSG_SETCONTROL(skb->data, contr);
}
 
cmtp_send_capimsg(session, skb);
}
 
static char *cmtp_procinfo(struct capi_ctr *ctrl)
{
return "CAPI Message Transport Protocol";
}
 
static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *app;
struct list_head *p, *n;
int len = 0;
 
len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION);
len += sprintf(page + len, "addr %s\n", session->name);
len += sprintf(page + len, "ctrl %d\n", session->num);
 
list_for_each_safe(p, n, &session->applications) {
app = list_entry(p, struct cmtp_application, list);
len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
}
 
if (off + count >= len)
*eof = 1;
 
if (len < off)
return 0;
 
*start = page + off;
 
return ((count < len - off) ? count : len - off);
}
 
static struct capi_driver cmtp_driver = {
name: "cmtp",
revision: REVISION,
load_firmware: cmtp_load_firmware,
reset_ctr: cmtp_reset_ctr,
remove_ctr: cmtp_remove_ctr,
register_appl: cmtp_register_appl,
release_appl: cmtp_release_appl,
send_message: cmtp_send_message,
procinfo: cmtp_procinfo,
ctr_read_proc: cmtp_ctr_read_proc,
 
driver_read_proc: 0,
add_card: 0,
};
 
 
int cmtp_attach_device(struct cmtp_session *session)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long timeo = CMTP_INTEROP_TIMEOUT;
unsigned char buf[4];
 
BT_DBG("session %p", session);
 
capimsg_setu32(buf, 0, 0);
 
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
CAPI_FUNCTION_GET_PROFILE, buf, 4);
 
add_wait_queue(&session->wait, &wait);
while (timeo) {
set_current_state(TASK_INTERRUPTIBLE);
 
if (session->ncontroller)
break;
 
if (signal_pending(current))
break;
 
timeo = schedule_timeout(timeo);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&session->wait, &wait);
 
BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
 
if (!timeo)
return -ETIMEDOUT;
 
if (!session->ncontroller)
return -ENODEV;
 
 
if (session->ncontroller > 1)
BT_INFO("Setting up only CAPI controller 1");
 
if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) {
BT_ERR("Can't attach new controller");
return -EBUSY;
}
 
session->num = session->ctrl->cnr;
 
BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num);
 
capimsg_setu32(buf, 0, 1);
 
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
 
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_VERSION, buf, 4);
 
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
 
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_PROFILE, buf, 4);
 
return 0;
}
 
void cmtp_detach_device(struct cmtp_session *session)
{
struct capi_ctr *ctrl = session->ctrl;
 
BT_DBG("session %p ctrl %p", session, ctrl);
 
if (!ctrl)
return;
 
ctrl->reseted(ctrl);
 
di->detach_ctr(ctrl);
}
 
int cmtp_init_capi(void)
{
if (!(di = attach_capi_driver(&cmtp_driver))) {
BT_ERR("Can't attach CAPI driver");
return -EIO;
}
 
return 0;
}
 
void cmtp_cleanup_capi(void)
{
detach_capi_driver(&cmtp_driver);
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.