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(¤t->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); |
} |