diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/bluetooth/cmtp | |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/bluetooth/cmtp')
| -rw-r--r-- | net/bluetooth/cmtp/Kconfig | 11 | ||||
| -rw-r--r-- | net/bluetooth/cmtp/Makefile | 7 | ||||
| -rw-r--r-- | net/bluetooth/cmtp/capi.c | 600 | ||||
| -rw-r--r-- | net/bluetooth/cmtp/cmtp.h | 135 | ||||
| -rw-r--r-- | net/bluetooth/cmtp/core.c | 504 | ||||
| -rw-r--r-- | net/bluetooth/cmtp/sock.c | 226 |
6 files changed, 1483 insertions, 0 deletions
diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig new file mode 100644 index 00000000000..d6b0382f6f3 --- /dev/null +++ b/net/bluetooth/cmtp/Kconfig | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | config BT_CMTP | ||
| 2 | tristate "CMTP protocol support" | ||
| 3 | depends on BT && BT_L2CAP && ISDN_CAPI | ||
| 4 | help | ||
| 5 | CMTP (CAPI Message Transport Protocol) is a transport layer | ||
| 6 | for CAPI messages. CMTP is required for the Bluetooth Common | ||
| 7 | ISDN Access Profile. | ||
| 8 | |||
| 9 | Say Y here to compile CMTP support into the kernel or say M to | ||
| 10 | compile it as module (cmtp). | ||
| 11 | |||
diff --git a/net/bluetooth/cmtp/Makefile b/net/bluetooth/cmtp/Makefile new file mode 100644 index 00000000000..890a9a5a686 --- /dev/null +++ b/net/bluetooth/cmtp/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the Linux Bluetooth CMTP layer | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_BT_CMTP) += cmtp.o | ||
| 6 | |||
| 7 | cmtp-objs := core.o sock.o capi.o | ||
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c new file mode 100644 index 00000000000..1e5c030b72a --- /dev/null +++ b/net/bluetooth/cmtp/capi.c | |||
| @@ -0,0 +1,600 @@ | |||
| 1 | /* | ||
| 2 | CMTP implementation for Linux Bluetooth stack (BlueZ). | ||
| 3 | Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License version 2 as | ||
| 7 | published by the Free Software Foundation; | ||
| 8 | |||
| 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
| 10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
| 12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
| 13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
| 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 17 | |||
| 18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
| 19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
| 20 | SOFTWARE IS DISCLAIMED. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/config.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | |||
| 26 | #include <linux/types.h> | ||
| 27 | #include <linux/errno.h> | ||
| 28 | #include <linux/kernel.h> | ||
| 29 | #include <linux/major.h> | ||
| 30 | #include <linux/sched.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/poll.h> | ||
| 33 | #include <linux/fcntl.h> | ||
| 34 | #include <linux/skbuff.h> | ||
| 35 | #include <linux/socket.h> | ||
| 36 | #include <linux/ioctl.h> | ||
| 37 | #include <linux/file.h> | ||
| 38 | #include <linux/wait.h> | ||
| 39 | #include <net/sock.h> | ||
| 40 | |||
| 41 | #include <linux/isdn/capilli.h> | ||
| 42 | #include <linux/isdn/capicmd.h> | ||
| 43 | #include <linux/isdn/capiutil.h> | ||
| 44 | |||
| 45 | #include "cmtp.h" | ||
| 46 | |||
| 47 | #ifndef CONFIG_BT_CMTP_DEBUG | ||
| 48 | #undef BT_DBG | ||
| 49 | #define BT_DBG(D...) | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #define CAPI_INTEROPERABILITY 0x20 | ||
| 53 | |||
| 54 | #define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) | ||
| 55 | #define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) | ||
| 56 | #define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) | ||
| 57 | #define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) | ||
| 58 | |||
| 59 | #define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) | ||
| 60 | #define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) | ||
| 61 | #define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) | ||
| 62 | #define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) | ||
| 63 | |||
| 64 | #define CAPI_FUNCTION_REGISTER 0 | ||
| 65 | #define CAPI_FUNCTION_RELEASE 1 | ||
| 66 | #define CAPI_FUNCTION_GET_PROFILE 2 | ||
| 67 | #define CAPI_FUNCTION_GET_MANUFACTURER 3 | ||
| 68 | #define CAPI_FUNCTION_GET_VERSION 4 | ||
| 69 | #define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 | ||
| 70 | #define CAPI_FUNCTION_MANUFACTURER 6 | ||
| 71 | #define CAPI_FUNCTION_LOOPBACK 7 | ||
| 72 | |||
| 73 | |||
| 74 | #define CMTP_MSGNUM 1 | ||
| 75 | #define CMTP_APPLID 2 | ||
| 76 | #define CMTP_MAPPING 3 | ||
| 77 | |||
| 78 | static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) | ||
| 79 | { | ||
| 80 | struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL); | ||
| 81 | |||
| 82 | BT_DBG("session %p application %p appl %d", session, app, appl); | ||
| 83 | |||
| 84 | if (!app) | ||
| 85 | return NULL; | ||
| 86 | |||
| 87 | memset(app, 0, sizeof(*app)); | ||
| 88 | |||
| 89 | app->state = BT_OPEN; | ||
| 90 | app->appl = appl; | ||
| 91 | |||
| 92 | list_add_tail(&app->list, &session->applications); | ||
| 93 | |||
| 94 | return app; | ||
| 95 | } | ||
| 96 | |||
| 97 | static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) | ||
| 98 | { | ||
| 99 | BT_DBG("session %p application %p", session, app); | ||
| 100 | |||
| 101 | if (app) { | ||
| 102 | list_del(&app->list); | ||
| 103 | kfree(app); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) | ||
| 108 | { | ||
| 109 | struct cmtp_application *app; | ||
| 110 | struct list_head *p, *n; | ||
| 111 | |||
| 112 | list_for_each_safe(p, n, &session->applications) { | ||
| 113 | app = list_entry(p, struct cmtp_application, list); | ||
| 114 | switch (pattern) { | ||
| 115 | case CMTP_MSGNUM: | ||
| 116 | if (app->msgnum == value) | ||
| 117 | return app; | ||
| 118 | break; | ||
| 119 | case CMTP_APPLID: | ||
| 120 | if (app->appl == value) | ||
| 121 | return app; | ||
| 122 | break; | ||
| 123 | case CMTP_MAPPING: | ||
| 124 | if (app->mapping == value) | ||
| 125 | return app; | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | return NULL; | ||
| 131 | } | ||
| 132 | |||
| 133 | static int cmtp_msgnum_get(struct cmtp_session *session) | ||
| 134 | { | ||
| 135 | session->msgnum++; | ||
| 136 | |||
| 137 | if ((session->msgnum & 0xff) > 200) | ||
| 138 | session->msgnum = CMTP_INITIAL_MSGNUM + 1; | ||
| 139 | |||
| 140 | return session->msgnum; | ||
| 141 | } | ||
| 142 | |||
| 143 | static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) | ||
| 144 | { | ||
| 145 | struct cmtp_scb *scb = (void *) skb->cb; | ||
| 146 | |||
| 147 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | ||
| 148 | |||
| 149 | scb->id = -1; | ||
| 150 | scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); | ||
| 151 | |||
| 152 | skb_queue_tail(&session->transmit, skb); | ||
| 153 | |||
| 154 | cmtp_schedule(session); | ||
| 155 | } | ||
| 156 | |||
| 157 | static void cmtp_send_interopmsg(struct cmtp_session *session, | ||
| 158 | __u8 subcmd, __u16 appl, __u16 msgnum, | ||
| 159 | __u16 function, unsigned char *buf, int len) | ||
| 160 | { | ||
| 161 | struct sk_buff *skb; | ||
| 162 | unsigned char *s; | ||
| 163 | |||
| 164 | BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); | ||
| 165 | |||
| 166 | if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) { | ||
| 167 | BT_ERR("Can't allocate memory for interoperability packet"); | ||
| 168 | return; | ||
| 169 | } | ||
| 170 | |||
| 171 | s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); | ||
| 172 | |||
| 173 | capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); | ||
| 174 | capimsg_setu16(s, 2, appl); | ||
| 175 | capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); | ||
| 176 | capimsg_setu8 (s, 5, subcmd); | ||
| 177 | capimsg_setu16(s, 6, msgnum); | ||
| 178 | |||
| 179 | /* Interoperability selector (Bluetooth Device Management) */ | ||
| 180 | capimsg_setu16(s, 8, 0x0001); | ||
| 181 | |||
| 182 | capimsg_setu8 (s, 10, 3 + len); | ||
| 183 | capimsg_setu16(s, 11, function); | ||
| 184 | capimsg_setu8 (s, 13, len); | ||
| 185 | |||
| 186 | if (len > 0) | ||
| 187 | memcpy(s + 14, buf, len); | ||
| 188 | |||
| 189 | cmtp_send_capimsg(session, skb); | ||
| 190 | } | ||
| 191 | |||
| 192 | static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) | ||
| 193 | { | ||
| 194 | struct capi_ctr *ctrl = &session->ctrl; | ||
| 195 | struct cmtp_application *application; | ||
| 196 | __u16 appl, msgnum, func, info; | ||
| 197 | __u32 controller; | ||
| 198 | |||
| 199 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | ||
| 200 | |||
| 201 | switch (CAPIMSG_SUBCOMMAND(skb->data)) { | ||
| 202 | case CAPI_CONF: | ||
| 203 | func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); | ||
| 204 | info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); | ||
| 205 | |||
| 206 | switch (func) { | ||
| 207 | case CAPI_FUNCTION_REGISTER: | ||
| 208 | msgnum = CAPIMSG_MSGID(skb->data); | ||
| 209 | |||
| 210 | application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); | ||
| 211 | if (application) { | ||
| 212 | application->state = BT_CONNECTED; | ||
| 213 | application->msgnum = 0; | ||
| 214 | application->mapping = CAPIMSG_APPID(skb->data); | ||
| 215 | wake_up_interruptible(&session->wait); | ||
| 216 | } | ||
| 217 | |||
| 218 | break; | ||
| 219 | |||
| 220 | case CAPI_FUNCTION_RELEASE: | ||
| 221 | appl = CAPIMSG_APPID(skb->data); | ||
| 222 | |||
| 223 | application = cmtp_application_get(session, CMTP_MAPPING, appl); | ||
| 224 | if (application) { | ||
| 225 | application->state = BT_CLOSED; | ||
| 226 | application->msgnum = 0; | ||
| 227 | wake_up_interruptible(&session->wait); | ||
| 228 | } | ||
| 229 | |||
| 230 | break; | ||
| 231 | |||
| 232 | case CAPI_FUNCTION_GET_PROFILE: | ||
| 233 | controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); | ||
| 234 | msgnum = CAPIMSG_MSGID(skb->data); | ||
| 235 | |||
| 236 | if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { | ||
| 237 | session->ncontroller = controller; | ||
| 238 | wake_up_interruptible(&session->wait); | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | |||
| 242 | if (!info && ctrl) { | ||
| 243 | memcpy(&ctrl->profile, | ||
| 244 | skb->data + CAPI_MSG_BASELEN + 11, | ||
| 245 | sizeof(capi_profile)); | ||
| 246 | session->state = BT_CONNECTED; | ||
| 247 | capi_ctr_ready(ctrl); | ||
| 248 | } | ||
| 249 | |||
| 250 | break; | ||
| 251 | |||
| 252 | case CAPI_FUNCTION_GET_MANUFACTURER: | ||
| 253 | controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10); | ||
| 254 | |||
| 255 | if (!info && ctrl) { | ||
| 256 | strncpy(ctrl->manu, | ||
| 257 | skb->data + CAPI_MSG_BASELEN + 15, | ||
| 258 | skb->data[CAPI_MSG_BASELEN + 14]); | ||
| 259 | } | ||
| 260 | |||
| 261 | break; | ||
| 262 | |||
| 263 | case CAPI_FUNCTION_GET_VERSION: | ||
| 264 | controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); | ||
| 265 | |||
| 266 | if (!info && ctrl) { | ||
| 267 | ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); | ||
| 268 | ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); | ||
| 269 | ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); | ||
| 270 | ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); | ||
| 271 | } | ||
| 272 | |||
| 273 | break; | ||
| 274 | |||
| 275 | case CAPI_FUNCTION_GET_SERIAL_NUMBER: | ||
| 276 | controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); | ||
| 277 | |||
| 278 | if (!info && ctrl) { | ||
| 279 | memset(ctrl->serial, 0, CAPI_SERIAL_LEN); | ||
| 280 | strncpy(ctrl->serial, | ||
| 281 | skb->data + CAPI_MSG_BASELEN + 17, | ||
| 282 | skb->data[CAPI_MSG_BASELEN + 16]); | ||
| 283 | } | ||
| 284 | |||
| 285 | break; | ||
| 286 | } | ||
| 287 | |||
| 288 | break; | ||
| 289 | |||
| 290 | case CAPI_IND: | ||
| 291 | func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); | ||
| 292 | |||
| 293 | if (func == CAPI_FUNCTION_LOOPBACK) { | ||
| 294 | appl = CAPIMSG_APPID(skb->data); | ||
| 295 | msgnum = CAPIMSG_MSGID(skb->data); | ||
| 296 | cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, | ||
| 297 | skb->data + CAPI_MSG_BASELEN + 6, | ||
| 298 | skb->data[CAPI_MSG_BASELEN + 5]); | ||
| 299 | } | ||
| 300 | |||
| 301 | break; | ||
| 302 | } | ||
| 303 | |||
| 304 | kfree_skb(skb); | ||
| 305 | } | ||
| 306 | |||
| 307 | void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) | ||
| 308 | { | ||
| 309 | struct capi_ctr *ctrl = &session->ctrl; | ||
| 310 | struct cmtp_application *application; | ||
| 311 | __u16 cmd, appl; | ||
| 312 | __u32 contr; | ||
| 313 | |||
| 314 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | ||
| 315 | |||
| 316 | if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { | ||
| 317 | cmtp_recv_interopmsg(session, skb); | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | |||
| 321 | if (session->flags & (1 << CMTP_LOOPBACK)) { | ||
| 322 | kfree_skb(skb); | ||
| 323 | return; | ||
| 324 | } | ||
| 325 | |||
| 326 | cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); | ||
| 327 | appl = CAPIMSG_APPID(skb->data); | ||
| 328 | contr = CAPIMSG_CONTROL(skb->data); | ||
| 329 | |||
| 330 | application = cmtp_application_get(session, CMTP_MAPPING, appl); | ||
| 331 | if (application) { | ||
| 332 | appl = application->appl; | ||
| 333 | CAPIMSG_SETAPPID(skb->data, appl); | ||
| 334 | } else { | ||
| 335 | BT_ERR("Can't find application with id %d", appl); | ||
| 336 | kfree_skb(skb); | ||
| 337 | return; | ||
| 338 | } | ||
| 339 | |||
| 340 | if ((contr & 0x7f) == 0x01) { | ||
| 341 | contr = (contr & 0xffffff80) | session->num; | ||
| 342 | CAPIMSG_SETCONTROL(skb->data, contr); | ||
| 343 | } | ||
| 344 | |||
| 345 | if (!ctrl) { | ||
| 346 | BT_ERR("Can't find controller %d for message", session->num); | ||
| 347 | kfree_skb(skb); | ||
| 348 | return; | ||
| 349 | } | ||
| 350 | |||
| 351 | capi_ctr_handle_message(ctrl, appl, skb); | ||
| 352 | } | ||
| 353 | |||
| 354 | static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) | ||
| 355 | { | ||
| 356 | BT_DBG("ctrl %p data %p", ctrl, data); | ||
| 357 | |||
| 358 | return 0; | ||
| 359 | } | ||
| 360 | |||
| 361 | static void cmtp_reset_ctr(struct capi_ctr *ctrl) | ||
| 362 | { | ||
| 363 | struct cmtp_session *session = ctrl->driverdata; | ||
| 364 | |||
| 365 | BT_DBG("ctrl %p", ctrl); | ||
| 366 | |||
| 367 | capi_ctr_reseted(ctrl); | ||
| 368 | |||
| 369 | atomic_inc(&session->terminate); | ||
| 370 | cmtp_schedule(session); | ||
| 371 | } | ||
| 372 | |||
| 373 | static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) | ||
| 374 | { | ||
| 375 | DECLARE_WAITQUEUE(wait, current); | ||
| 376 | struct cmtp_session *session = ctrl->driverdata; | ||
| 377 | struct cmtp_application *application; | ||
| 378 | unsigned long timeo = CMTP_INTEROP_TIMEOUT; | ||
| 379 | unsigned char buf[8]; | ||
| 380 | int err = 0, nconn, want = rp->level3cnt; | ||
| 381 | |||
| 382 | BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d", | ||
| 383 | ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); | ||
| 384 | |||
| 385 | application = cmtp_application_add(session, appl); | ||
| 386 | if (!application) { | ||
| 387 | BT_ERR("Can't allocate memory for new application"); | ||
| 388 | return; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (want < 0) | ||
| 392 | nconn = ctrl->profile.nbchannel * -want; | ||
| 393 | else | ||
| 394 | nconn = want; | ||
| 395 | |||
| 396 | if (nconn == 0) | ||
| 397 | nconn = ctrl->profile.nbchannel; | ||
| 398 | |||
| 399 | capimsg_setu16(buf, 0, nconn); | ||
| 400 | capimsg_setu16(buf, 2, rp->datablkcnt); | ||
| 401 | capimsg_setu16(buf, 4, rp->datablklen); | ||
| 402 | |||
| 403 | application->state = BT_CONFIG; | ||
| 404 | application->msgnum = cmtp_msgnum_get(session); | ||
| 405 | |||
| 406 | cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, | ||
| 407 | CAPI_FUNCTION_REGISTER, buf, 6); | ||
| 408 | |||
| 409 | add_wait_queue(&session->wait, &wait); | ||
| 410 | while (1) { | ||
| 411 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 412 | |||
| 413 | if (!timeo) { | ||
| 414 | err = -EAGAIN; | ||
| 415 | break; | ||
| 416 | } | ||
| 417 | |||
| 418 | if (application->state == BT_CLOSED) { | ||
| 419 | err = -application->err; | ||
| 420 | break; | ||
| 421 | } | ||
| 422 | |||
| 423 | if (application->state == BT_CONNECTED) | ||
| 424 | break; | ||
| 425 | |||
| 426 | if (signal_pending(current)) { | ||
| 427 | err = -EINTR; | ||
| 428 | break; | ||
| 429 | } | ||
| 430 | |||
| 431 | timeo = schedule_timeout(timeo); | ||
| 432 | } | ||
| 433 | set_current_state(TASK_RUNNING); | ||
| 434 | remove_wait_queue(&session->wait, &wait); | ||
| 435 | |||
| 436 | if (err) { | ||
| 437 | cmtp_application_del(session, application); | ||
| 438 | return; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) | ||
| 443 | { | ||
| 444 | struct cmtp_session *session = ctrl->driverdata; | ||
| 445 | struct cmtp_application *application; | ||
| 446 | |||
| 447 | BT_DBG("ctrl %p appl %d", ctrl, appl); | ||
| 448 | |||
| 449 | application = cmtp_application_get(session, CMTP_APPLID, appl); | ||
| 450 | if (!application) { | ||
| 451 | BT_ERR("Can't find application"); | ||
| 452 | return; | ||
| 453 | } | ||
| 454 | |||
| 455 | application->msgnum = cmtp_msgnum_get(session); | ||
| 456 | |||
| 457 | cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, | ||
| 458 | CAPI_FUNCTION_RELEASE, NULL, 0); | ||
| 459 | |||
| 460 | wait_event_interruptible_timeout(session->wait, | ||
| 461 | (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT); | ||
| 462 | |||
| 463 | cmtp_application_del(session, application); | ||
| 464 | } | ||
| 465 | |||
| 466 | static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) | ||
| 467 | { | ||
| 468 | struct cmtp_session *session = ctrl->driverdata; | ||
| 469 | struct cmtp_application *application; | ||
| 470 | __u16 appl; | ||
| 471 | __u32 contr; | ||
| 472 | |||
| 473 | BT_DBG("ctrl %p skb %p", ctrl, skb); | ||
| 474 | |||
| 475 | appl = CAPIMSG_APPID(skb->data); | ||
| 476 | contr = CAPIMSG_CONTROL(skb->data); | ||
| 477 | |||
| 478 | application = cmtp_application_get(session, CMTP_APPLID, appl); | ||
| 479 | if ((!application) || (application->state != BT_CONNECTED)) { | ||
| 480 | BT_ERR("Can't find application with id %d", appl); | ||
| 481 | return CAPI_ILLAPPNR; | ||
| 482 | } | ||
| 483 | |||
| 484 | CAPIMSG_SETAPPID(skb->data, application->mapping); | ||
| 485 | |||
| 486 | if ((contr & 0x7f) == session->num) { | ||
| 487 | contr = (contr & 0xffffff80) | 0x01; | ||
| 488 | CAPIMSG_SETCONTROL(skb->data, contr); | ||
| 489 | } | ||
| 490 | |||
| 491 | cmtp_send_capimsg(session, skb); | ||
| 492 | |||
| 493 | return CAPI_NOERROR; | ||
| 494 | } | ||
| 495 | |||
| 496 | static char *cmtp_procinfo(struct capi_ctr *ctrl) | ||
| 497 | { | ||
| 498 | return "CAPI Message Transport Protocol"; | ||
| 499 | } | ||
| 500 | |||
| 501 | static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) | ||
| 502 | { | ||
| 503 | struct cmtp_session *session = ctrl->driverdata; | ||
| 504 | struct cmtp_application *app; | ||
| 505 | struct list_head *p, *n; | ||
| 506 | int len = 0; | ||
| 507 | |||
| 508 | len += sprintf(page + len, "%s\n\n", cmtp_procinfo(ctrl)); | ||
| 509 | len += sprintf(page + len, "addr %s\n", session->name); | ||
| 510 | len += sprintf(page + len, "ctrl %d\n", session->num); | ||
| 511 | |||
| 512 | list_for_each_safe(p, n, &session->applications) { | ||
| 513 | app = list_entry(p, struct cmtp_application, list); | ||
| 514 | len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping); | ||
| 515 | } | ||
| 516 | |||
| 517 | if (off + count >= len) | ||
| 518 | *eof = 1; | ||
| 519 | |||
| 520 | if (len < off) | ||
| 521 | return 0; | ||
| 522 | |||
| 523 | *start = page + off; | ||
| 524 | |||
| 525 | return ((count < len - off) ? count : len - off); | ||
| 526 | } | ||
| 527 | |||
| 528 | |||
| 529 | int cmtp_attach_device(struct cmtp_session *session) | ||
| 530 | { | ||
| 531 | unsigned char buf[4]; | ||
| 532 | long ret; | ||
| 533 | |||
| 534 | BT_DBG("session %p", session); | ||
| 535 | |||
| 536 | capimsg_setu32(buf, 0, 0); | ||
| 537 | |||
| 538 | cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, | ||
| 539 | CAPI_FUNCTION_GET_PROFILE, buf, 4); | ||
| 540 | |||
| 541 | ret = wait_event_interruptible_timeout(session->wait, | ||
| 542 | session->ncontroller, CMTP_INTEROP_TIMEOUT); | ||
| 543 | |||
| 544 | BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); | ||
| 545 | |||
| 546 | if (!ret) | ||
| 547 | return -ETIMEDOUT; | ||
| 548 | |||
| 549 | if (!session->ncontroller) | ||
| 550 | return -ENODEV; | ||
| 551 | |||
| 552 | if (session->ncontroller > 1) | ||
| 553 | BT_INFO("Setting up only CAPI controller 1"); | ||
| 554 | |||
| 555 | session->ctrl.owner = THIS_MODULE; | ||
| 556 | session->ctrl.driverdata = session; | ||
| 557 | strcpy(session->ctrl.name, session->name); | ||
| 558 | |||
| 559 | session->ctrl.driver_name = "cmtp"; | ||
| 560 | session->ctrl.load_firmware = cmtp_load_firmware; | ||
| 561 | session->ctrl.reset_ctr = cmtp_reset_ctr; | ||
| 562 | session->ctrl.register_appl = cmtp_register_appl; | ||
| 563 | session->ctrl.release_appl = cmtp_release_appl; | ||
| 564 | session->ctrl.send_message = cmtp_send_message; | ||
| 565 | |||
| 566 | session->ctrl.procinfo = cmtp_procinfo; | ||
| 567 | session->ctrl.ctr_read_proc = cmtp_ctr_read_proc; | ||
| 568 | |||
| 569 | if (attach_capi_ctr(&session->ctrl) < 0) { | ||
| 570 | BT_ERR("Can't attach new controller"); | ||
| 571 | return -EBUSY; | ||
| 572 | } | ||
| 573 | |||
| 574 | session->num = session->ctrl.cnr; | ||
| 575 | |||
| 576 | BT_DBG("session %p num %d", session, session->num); | ||
| 577 | |||
| 578 | capimsg_setu32(buf, 0, 1); | ||
| 579 | |||
| 580 | cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), | ||
| 581 | CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); | ||
| 582 | |||
| 583 | cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), | ||
| 584 | CAPI_FUNCTION_GET_VERSION, buf, 4); | ||
| 585 | |||
| 586 | cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), | ||
| 587 | CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); | ||
| 588 | |||
| 589 | cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), | ||
| 590 | CAPI_FUNCTION_GET_PROFILE, buf, 4); | ||
| 591 | |||
| 592 | return 0; | ||
| 593 | } | ||
| 594 | |||
| 595 | void cmtp_detach_device(struct cmtp_session *session) | ||
| 596 | { | ||
| 597 | BT_DBG("session %p", session); | ||
| 598 | |||
| 599 | detach_capi_ctr(&session->ctrl); | ||
| 600 | } | ||
diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h new file mode 100644 index 00000000000..40e3dfec0cc --- /dev/null +++ b/net/bluetooth/cmtp/cmtp.h | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | /* | ||
| 2 | CMTP implementation for Linux Bluetooth stack (BlueZ). | ||
| 3 | Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License version 2 as | ||
| 7 | published by the Free Software Foundation; | ||
| 8 | |||
| 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
| 10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
| 12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
| 13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
| 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 17 | |||
| 18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
| 19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
| 20 | SOFTWARE IS DISCLAIMED. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __CMTP_H | ||
| 24 | #define __CMTP_H | ||
| 25 | |||
| 26 | #include <linux/types.h> | ||
| 27 | #include <net/bluetooth/bluetooth.h> | ||
| 28 | |||
| 29 | #define BTNAMSIZ 18 | ||
| 30 | |||
| 31 | /* CMTP ioctl defines */ | ||
| 32 | #define CMTPCONNADD _IOW('C', 200, int) | ||
| 33 | #define CMTPCONNDEL _IOW('C', 201, int) | ||
| 34 | #define CMTPGETCONNLIST _IOR('C', 210, int) | ||
| 35 | #define CMTPGETCONNINFO _IOR('C', 211, int) | ||
| 36 | |||
| 37 | #define CMTP_LOOPBACK 0 | ||
| 38 | |||
| 39 | struct cmtp_connadd_req { | ||
| 40 | int sock; // Connected socket | ||
| 41 | __u32 flags; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct cmtp_conndel_req { | ||
| 45 | bdaddr_t bdaddr; | ||
| 46 | __u32 flags; | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct cmtp_conninfo { | ||
| 50 | bdaddr_t bdaddr; | ||
| 51 | __u32 flags; | ||
| 52 | __u16 state; | ||
| 53 | int num; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct cmtp_connlist_req { | ||
| 57 | __u32 cnum; | ||
| 58 | struct cmtp_conninfo __user *ci; | ||
| 59 | }; | ||
| 60 | |||
| 61 | int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); | ||
| 62 | int cmtp_del_connection(struct cmtp_conndel_req *req); | ||
| 63 | int cmtp_get_connlist(struct cmtp_connlist_req *req); | ||
| 64 | int cmtp_get_conninfo(struct cmtp_conninfo *ci); | ||
| 65 | |||
| 66 | /* CMTP session defines */ | ||
| 67 | #define CMTP_INTEROP_TIMEOUT (HZ * 5) | ||
| 68 | #define CMTP_INITIAL_MSGNUM 0xff00 | ||
| 69 | |||
| 70 | struct cmtp_session { | ||
| 71 | struct list_head list; | ||
| 72 | |||
| 73 | struct socket *sock; | ||
| 74 | |||
| 75 | bdaddr_t bdaddr; | ||
| 76 | |||
| 77 | unsigned long state; | ||
| 78 | unsigned long flags; | ||
| 79 | |||
| 80 | uint mtu; | ||
| 81 | |||
| 82 | char name[BTNAMSIZ]; | ||
| 83 | |||
| 84 | atomic_t terminate; | ||
| 85 | |||
| 86 | wait_queue_head_t wait; | ||
| 87 | |||
| 88 | int ncontroller; | ||
| 89 | int num; | ||
| 90 | struct capi_ctr ctrl; | ||
| 91 | |||
| 92 | struct list_head applications; | ||
| 93 | |||
| 94 | unsigned long blockids; | ||
| 95 | int msgnum; | ||
| 96 | |||
| 97 | struct sk_buff_head transmit; | ||
| 98 | |||
| 99 | struct sk_buff *reassembly[16]; | ||
| 100 | }; | ||
| 101 | |||
| 102 | struct cmtp_application { | ||
| 103 | struct list_head list; | ||
| 104 | |||
| 105 | unsigned long state; | ||
| 106 | int err; | ||
| 107 | |||
| 108 | __u16 appl; | ||
| 109 | __u16 mapping; | ||
| 110 | |||
| 111 | __u16 msgnum; | ||
| 112 | }; | ||
| 113 | |||
| 114 | struct cmtp_scb { | ||
| 115 | int id; | ||
| 116 | int data; | ||
| 117 | }; | ||
| 118 | |||
| 119 | int cmtp_attach_device(struct cmtp_session *session); | ||
| 120 | void cmtp_detach_device(struct cmtp_session *session); | ||
| 121 | |||
| 122 | void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); | ||
| 123 | |||
| 124 | static inline void cmtp_schedule(struct cmtp_session *session) | ||
| 125 | { | ||
| 126 | struct sock *sk = session->sock->sk; | ||
| 127 | |||
| 128 | wake_up_interruptible(sk->sk_sleep); | ||
| 129 | } | ||
| 130 | |||
| 131 | /* CMTP init defines */ | ||
| 132 | int cmtp_init_sockets(void); | ||
| 133 | void cmtp_cleanup_sockets(void); | ||
| 134 | |||
| 135 | #endif /* __CMTP_H */ | ||
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c new file mode 100644 index 00000000000..20ce04f2be8 --- /dev/null +++ b/net/bluetooth/cmtp/core.c | |||
| @@ -0,0 +1,504 @@ | |||
| 1 | /* | ||
| 2 | CMTP implementation for Linux Bluetooth stack (BlueZ). | ||
| 3 | Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License version 2 as | ||
| 7 | published by the Free Software Foundation; | ||
| 8 | |||
| 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
| 10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
| 12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
| 13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
| 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 17 | |||
| 18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
| 19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
| 20 | SOFTWARE IS DISCLAIMED. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/config.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | |||
| 26 | #include <linux/types.h> | ||
| 27 | #include <linux/errno.h> | ||
| 28 | #include <linux/kernel.h> | ||
| 29 | #include <linux/major.h> | ||
| 30 | #include <linux/sched.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/poll.h> | ||
| 33 | #include <linux/fcntl.h> | ||
| 34 | #include <linux/skbuff.h> | ||
| 35 | #include <linux/socket.h> | ||
| 36 | #include <linux/ioctl.h> | ||
| 37 | #include <linux/file.h> | ||
| 38 | #include <linux/init.h> | ||
| 39 | #include <net/sock.h> | ||
| 40 | |||
| 41 | #include <linux/isdn/capilli.h> | ||
| 42 | |||
| 43 | #include <net/bluetooth/bluetooth.h> | ||
| 44 | #include <net/bluetooth/l2cap.h> | ||
| 45 | |||
| 46 | #include "cmtp.h" | ||
| 47 | |||
| 48 | #ifndef CONFIG_BT_CMTP_DEBUG | ||
| 49 | #undef BT_DBG | ||
| 50 | #define BT_DBG(D...) | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #define VERSION "1.0" | ||
| 54 | |||
| 55 | static DECLARE_RWSEM(cmtp_session_sem); | ||
| 56 | static LIST_HEAD(cmtp_session_list); | ||
| 57 | |||
| 58 | static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) | ||
| 59 | { | ||
| 60 | struct cmtp_session *session; | ||
| 61 | struct list_head *p; | ||
| 62 | |||
| 63 | BT_DBG(""); | ||
| 64 | |||
| 65 | list_for_each(p, &cmtp_session_list) { | ||
| 66 | session = list_entry(p, struct cmtp_session, list); | ||
| 67 | if (!bacmp(bdaddr, &session->bdaddr)) | ||
| 68 | return session; | ||
| 69 | } | ||
| 70 | return NULL; | ||
| 71 | } | ||
| 72 | |||
| 73 | static void __cmtp_link_session(struct cmtp_session *session) | ||
| 74 | { | ||
| 75 | __module_get(THIS_MODULE); | ||
| 76 | list_add(&session->list, &cmtp_session_list); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void __cmtp_unlink_session(struct cmtp_session *session) | ||
| 80 | { | ||
| 81 | list_del(&session->list); | ||
| 82 | module_put(THIS_MODULE); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) | ||
| 86 | { | ||
| 87 | bacpy(&ci->bdaddr, &session->bdaddr); | ||
| 88 | |||
| 89 | ci->flags = session->flags; | ||
| 90 | ci->state = session->state; | ||
| 91 | |||
| 92 | ci->num = session->num; | ||
| 93 | } | ||
| 94 | |||
| 95 | |||
| 96 | static inline int cmtp_alloc_block_id(struct cmtp_session *session) | ||
| 97 | { | ||
| 98 | int i, id = -1; | ||
| 99 | |||
| 100 | for (i = 0; i < 16; i++) | ||
| 101 | if (!test_and_set_bit(i, &session->blockids)) { | ||
| 102 | id = i; | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | |||
| 106 | return id; | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline void cmtp_free_block_id(struct cmtp_session *session, int id) | ||
| 110 | { | ||
| 111 | clear_bit(id, &session->blockids); | ||
| 112 | } | ||
| 113 | |||
| 114 | static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) | ||
| 115 | { | ||
| 116 | struct sk_buff *skb = session->reassembly[id], *nskb; | ||
| 117 | int size; | ||
| 118 | |||
| 119 | BT_DBG("session %p buf %p count %d", session, buf, count); | ||
| 120 | |||
| 121 | size = (skb) ? skb->len + count : count; | ||
| 122 | |||
| 123 | if (!(nskb = alloc_skb(size, GFP_ATOMIC))) { | ||
| 124 | BT_ERR("Can't allocate memory for CAPI message"); | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | if (skb && (skb->len > 0)) | ||
| 129 | memcpy(skb_put(nskb, skb->len), skb->data, skb->len); | ||
| 130 | |||
| 131 | memcpy(skb_put(nskb, count), buf, count); | ||
| 132 | |||
| 133 | session->reassembly[id] = nskb; | ||
| 134 | |||
| 135 | if (skb) | ||
| 136 | kfree_skb(skb); | ||
| 137 | } | ||
| 138 | |||
| 139 | static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) | ||
| 140 | { | ||
| 141 | __u8 hdr, hdrlen, id; | ||
| 142 | __u16 len; | ||
| 143 | |||
| 144 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | ||
| 145 | |||
| 146 | while (skb->len > 0) { | ||
| 147 | hdr = skb->data[0]; | ||
| 148 | |||
| 149 | switch (hdr & 0xc0) { | ||
| 150 | case 0x40: | ||
| 151 | hdrlen = 2; | ||
| 152 | len = skb->data[1]; | ||
| 153 | break; | ||
| 154 | case 0x80: | ||
| 155 | hdrlen = 3; | ||
| 156 | len = skb->data[1] | (skb->data[2] << 8); | ||
| 157 | break; | ||
| 158 | default: | ||
| 159 | hdrlen = 1; | ||
| 160 | len = 0; | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | |||
| 164 | id = (hdr & 0x3c) >> 2; | ||
| 165 | |||
| 166 | BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); | ||
| 167 | |||
| 168 | if (hdrlen + len > skb->len) { | ||
| 169 | BT_ERR("Wrong size or header information in CMTP frame"); | ||
| 170 | break; | ||
| 171 | } | ||
| 172 | |||
| 173 | if (len == 0) { | ||
| 174 | skb_pull(skb, hdrlen); | ||
| 175 | continue; | ||
| 176 | } | ||
| 177 | |||
| 178 | switch (hdr & 0x03) { | ||
| 179 | case 0x00: | ||
| 180 | cmtp_add_msgpart(session, id, skb->data + hdrlen, len); | ||
| 181 | cmtp_recv_capimsg(session, session->reassembly[id]); | ||
| 182 | session->reassembly[id] = NULL; | ||
| 183 | break; | ||
| 184 | case 0x01: | ||
| 185 | cmtp_add_msgpart(session, id, skb->data + hdrlen, len); | ||
| 186 | break; | ||
| 187 | default: | ||
| 188 | if (session->reassembly[id] != NULL) | ||
| 189 | kfree_skb(session->reassembly[id]); | ||
| 190 | session->reassembly[id] = NULL; | ||
| 191 | break; | ||
| 192 | } | ||
| 193 | |||
| 194 | skb_pull(skb, hdrlen + len); | ||
| 195 | } | ||
| 196 | |||
| 197 | kfree_skb(skb); | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) | ||
| 202 | { | ||
| 203 | struct socket *sock = session->sock; | ||
| 204 | struct kvec iv = { data, len }; | ||
| 205 | struct msghdr msg; | ||
| 206 | |||
| 207 | BT_DBG("session %p data %p len %d", session, data, len); | ||
| 208 | |||
| 209 | if (!len) | ||
| 210 | return 0; | ||
| 211 | |||
| 212 | memset(&msg, 0, sizeof(msg)); | ||
| 213 | |||
| 214 | return kernel_sendmsg(sock, &msg, &iv, 1, len); | ||
| 215 | } | ||
| 216 | |||
| 217 | static int cmtp_process_transmit(struct cmtp_session *session) | ||
| 218 | { | ||
| 219 | struct sk_buff *skb, *nskb; | ||
| 220 | unsigned char *hdr; | ||
| 221 | unsigned int size, tail; | ||
| 222 | |||
| 223 | BT_DBG("session %p", session); | ||
| 224 | |||
| 225 | if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) { | ||
| 226 | BT_ERR("Can't allocate memory for new frame"); | ||
| 227 | return -ENOMEM; | ||
| 228 | } | ||
| 229 | |||
| 230 | while ((skb = skb_dequeue(&session->transmit))) { | ||
| 231 | struct cmtp_scb *scb = (void *) skb->cb; | ||
| 232 | |||
| 233 | if ((tail = (session->mtu - nskb->len)) < 5) { | ||
| 234 | cmtp_send_frame(session, nskb->data, nskb->len); | ||
| 235 | skb_trim(nskb, 0); | ||
| 236 | tail = session->mtu; | ||
| 237 | } | ||
| 238 | |||
| 239 | size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); | ||
| 240 | |||
| 241 | if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) { | ||
| 242 | skb_queue_head(&session->transmit, skb); | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | |||
| 246 | if (size < 256) { | ||
| 247 | hdr = skb_put(nskb, 2); | ||
| 248 | hdr[0] = 0x40 | ||
| 249 | | ((scb->id << 2) & 0x3c) | ||
| 250 | | ((skb->len == size) ? 0x00 : 0x01); | ||
| 251 | hdr[1] = size; | ||
| 252 | } else { | ||
| 253 | hdr = skb_put(nskb, 3); | ||
| 254 | hdr[0] = 0x80 | ||
| 255 | | ((scb->id << 2) & 0x3c) | ||
| 256 | | ((skb->len == size) ? 0x00 : 0x01); | ||
| 257 | hdr[1] = size & 0xff; | ||
| 258 | hdr[2] = size >> 8; | ||
| 259 | } | ||
| 260 | |||
| 261 | memcpy(skb_put(nskb, size), skb->data, size); | ||
| 262 | skb_pull(skb, size); | ||
| 263 | |||
| 264 | if (skb->len > 0) { | ||
| 265 | skb_queue_head(&session->transmit, skb); | ||
| 266 | } else { | ||
| 267 | cmtp_free_block_id(session, scb->id); | ||
| 268 | if (scb->data) { | ||
| 269 | cmtp_send_frame(session, nskb->data, nskb->len); | ||
| 270 | skb_trim(nskb, 0); | ||
| 271 | } | ||
| 272 | kfree_skb(skb); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | cmtp_send_frame(session, nskb->data, nskb->len); | ||
| 277 | |||
| 278 | kfree_skb(nskb); | ||
| 279 | |||
| 280 | return skb_queue_len(&session->transmit); | ||
| 281 | } | ||
| 282 | |||
| 283 | static int cmtp_session(void *arg) | ||
| 284 | { | ||
| 285 | struct cmtp_session *session = arg; | ||
| 286 | struct sock *sk = session->sock->sk; | ||
| 287 | struct sk_buff *skb; | ||
| 288 | wait_queue_t wait; | ||
| 289 | |||
| 290 | BT_DBG("session %p", session); | ||
| 291 | |||
| 292 | daemonize("kcmtpd_ctr_%d", session->num); | ||
| 293 | set_user_nice(current, -15); | ||
| 294 | current->flags |= PF_NOFREEZE; | ||
| 295 | |||
| 296 | init_waitqueue_entry(&wait, current); | ||
| 297 | add_wait_queue(sk->sk_sleep, &wait); | ||
| 298 | while (!atomic_read(&session->terminate)) { | ||
| 299 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 300 | |||
| 301 | if (sk->sk_state != BT_CONNECTED) | ||
| 302 | break; | ||
| 303 | |||
| 304 | while ((skb = skb_dequeue(&sk->sk_receive_queue))) { | ||
| 305 | skb_orphan(skb); | ||
| 306 | cmtp_recv_frame(session, skb); | ||
| 307 | } | ||
| 308 | |||
| 309 | cmtp_process_transmit(session); | ||
| 310 | |||
| 311 | schedule(); | ||
| 312 | } | ||
| 313 | set_current_state(TASK_RUNNING); | ||
| 314 | remove_wait_queue(sk->sk_sleep, &wait); | ||
| 315 | |||
| 316 | down_write(&cmtp_session_sem); | ||
| 317 | |||
| 318 | if (!(session->flags & (1 << CMTP_LOOPBACK))) | ||
| 319 | cmtp_detach_device(session); | ||
| 320 | |||
| 321 | fput(session->sock->file); | ||
| 322 | |||
| 323 | __cmtp_unlink_session(session); | ||
| 324 | |||
| 325 | up_write(&cmtp_session_sem); | ||
| 326 | |||
| 327 | kfree(session); | ||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | |||
| 331 | int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) | ||
| 332 | { | ||
| 333 | struct cmtp_session *session, *s; | ||
| 334 | bdaddr_t src, dst; | ||
| 335 | int i, err; | ||
| 336 | |||
| 337 | BT_DBG(""); | ||
| 338 | |||
| 339 | baswap(&src, &bt_sk(sock->sk)->src); | ||
| 340 | baswap(&dst, &bt_sk(sock->sk)->dst); | ||
| 341 | |||
| 342 | session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL); | ||
| 343 | if (!session) | ||
| 344 | return -ENOMEM; | ||
| 345 | memset(session, 0, sizeof(struct cmtp_session)); | ||
| 346 | |||
| 347 | down_write(&cmtp_session_sem); | ||
| 348 | |||
| 349 | s = __cmtp_get_session(&bt_sk(sock->sk)->dst); | ||
| 350 | if (s && s->state == BT_CONNECTED) { | ||
| 351 | err = -EEXIST; | ||
| 352 | goto failed; | ||
| 353 | } | ||
| 354 | |||
| 355 | bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst); | ||
| 356 | |||
| 357 | session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu); | ||
| 358 | |||
| 359 | BT_DBG("mtu %d", session->mtu); | ||
| 360 | |||
| 361 | sprintf(session->name, "%s", batostr(&dst)); | ||
| 362 | |||
| 363 | session->sock = sock; | ||
| 364 | session->state = BT_CONFIG; | ||
| 365 | |||
| 366 | init_waitqueue_head(&session->wait); | ||
| 367 | |||
| 368 | session->msgnum = CMTP_INITIAL_MSGNUM; | ||
| 369 | |||
| 370 | INIT_LIST_HEAD(&session->applications); | ||
| 371 | |||
| 372 | skb_queue_head_init(&session->transmit); | ||
| 373 | |||
| 374 | for (i = 0; i < 16; i++) | ||
| 375 | session->reassembly[i] = NULL; | ||
| 376 | |||
| 377 | session->flags = req->flags; | ||
| 378 | |||
| 379 | __cmtp_link_session(session); | ||
| 380 | |||
| 381 | err = kernel_thread(cmtp_session, session, CLONE_KERNEL); | ||
| 382 | if (err < 0) | ||
| 383 | goto unlink; | ||
| 384 | |||
| 385 | if (!(session->flags & (1 << CMTP_LOOPBACK))) { | ||
| 386 | err = cmtp_attach_device(session); | ||
| 387 | if (err < 0) | ||
| 388 | goto detach; | ||
| 389 | } | ||
| 390 | |||
| 391 | up_write(&cmtp_session_sem); | ||
| 392 | return 0; | ||
| 393 | |||
| 394 | detach: | ||
| 395 | cmtp_detach_device(session); | ||
| 396 | |||
| 397 | unlink: | ||
| 398 | __cmtp_unlink_session(session); | ||
| 399 | |||
| 400 | failed: | ||
| 401 | up_write(&cmtp_session_sem); | ||
| 402 | kfree(session); | ||
| 403 | return err; | ||
| 404 | } | ||
| 405 | |||
| 406 | int cmtp_del_connection(struct cmtp_conndel_req *req) | ||
| 407 | { | ||
| 408 | struct cmtp_session *session; | ||
| 409 | int err = 0; | ||
| 410 | |||
| 411 | BT_DBG(""); | ||
| 412 | |||
| 413 | down_read(&cmtp_session_sem); | ||
| 414 | |||
| 415 | session = __cmtp_get_session(&req->bdaddr); | ||
| 416 | if (session) { | ||
| 417 | /* Flush the transmit queue */ | ||
| 418 | skb_queue_purge(&session->transmit); | ||
| 419 | |||
| 420 | /* Kill session thread */ | ||
| 421 | atomic_inc(&session->terminate); | ||
| 422 | cmtp_schedule(session); | ||
| 423 | } else | ||
| 424 | err = -ENOENT; | ||
| 425 | |||
| 426 | up_read(&cmtp_session_sem); | ||
| 427 | return err; | ||
| 428 | } | ||
| 429 | |||
| 430 | int cmtp_get_connlist(struct cmtp_connlist_req *req) | ||
| 431 | { | ||
| 432 | struct list_head *p; | ||
| 433 | int err = 0, n = 0; | ||
| 434 | |||
| 435 | BT_DBG(""); | ||
| 436 | |||
| 437 | down_read(&cmtp_session_sem); | ||
| 438 | |||
| 439 | list_for_each(p, &cmtp_session_list) { | ||
| 440 | struct cmtp_session *session; | ||
| 441 | struct cmtp_conninfo ci; | ||
| 442 | |||
| 443 | session = list_entry(p, struct cmtp_session, list); | ||
| 444 | |||
| 445 | __cmtp_copy_session(session, &ci); | ||
| 446 | |||
| 447 | if (copy_to_user(req->ci, &ci, sizeof(ci))) { | ||
| 448 | err = -EFAULT; | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | |||
| 452 | if (++n >= req->cnum) | ||
| 453 | break; | ||
| 454 | |||
| 455 | req->ci++; | ||
| 456 | } | ||
| 457 | req->cnum = n; | ||
| 458 | |||
| 459 | up_read(&cmtp_session_sem); | ||
| 460 | return err; | ||
| 461 | } | ||
| 462 | |||
| 463 | int cmtp_get_conninfo(struct cmtp_conninfo *ci) | ||
| 464 | { | ||
| 465 | struct cmtp_session *session; | ||
| 466 | int err = 0; | ||
| 467 | |||
| 468 | down_read(&cmtp_session_sem); | ||
| 469 | |||
| 470 | session = __cmtp_get_session(&ci->bdaddr); | ||
| 471 | if (session) | ||
| 472 | __cmtp_copy_session(session, ci); | ||
| 473 | else | ||
| 474 | err = -ENOENT; | ||
| 475 | |||
| 476 | up_read(&cmtp_session_sem); | ||
| 477 | return err; | ||
| 478 | } | ||
| 479 | |||
| 480 | |||
| 481 | static int __init cmtp_init(void) | ||
| 482 | { | ||
| 483 | l2cap_load(); | ||
| 484 | |||
| 485 | BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION); | ||
| 486 | |||
| 487 | cmtp_init_sockets(); | ||
| 488 | |||
| 489 | return 0; | ||
| 490 | } | ||
| 491 | |||
| 492 | static void __exit cmtp_exit(void) | ||
| 493 | { | ||
| 494 | cmtp_cleanup_sockets(); | ||
| 495 | } | ||
| 496 | |||
| 497 | module_init(cmtp_init); | ||
| 498 | module_exit(cmtp_exit); | ||
| 499 | |||
| 500 | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); | ||
| 501 | MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION); | ||
| 502 | MODULE_VERSION(VERSION); | ||
| 503 | MODULE_LICENSE("GPL"); | ||
| 504 | MODULE_ALIAS("bt-proto-5"); | ||
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c new file mode 100644 index 00000000000..4c7f9e20dad --- /dev/null +++ b/net/bluetooth/cmtp/sock.c | |||
| @@ -0,0 +1,226 @@ | |||
| 1 | /* | ||
| 2 | CMTP implementation for Linux Bluetooth stack (BlueZ). | ||
| 3 | Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License version 2 as | ||
| 7 | published by the Free Software Foundation; | ||
| 8 | |||
| 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
| 10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
| 12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
| 13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
| 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 17 | |||
| 18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
| 19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
| 20 | SOFTWARE IS DISCLAIMED. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/config.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | |||
| 26 | #include <linux/types.h> | ||
| 27 | #include <linux/errno.h> | ||
| 28 | #include <linux/kernel.h> | ||
| 29 | #include <linux/major.h> | ||
| 30 | #include <linux/sched.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/poll.h> | ||
| 33 | #include <linux/fcntl.h> | ||
| 34 | #include <linux/skbuff.h> | ||
| 35 | #include <linux/socket.h> | ||
| 36 | #include <linux/ioctl.h> | ||
| 37 | #include <linux/file.h> | ||
| 38 | #include <net/sock.h> | ||
| 39 | |||
| 40 | #include <linux/isdn/capilli.h> | ||
| 41 | |||
| 42 | #include <asm/system.h> | ||
| 43 | #include <asm/uaccess.h> | ||
| 44 | |||
| 45 | #include "cmtp.h" | ||
| 46 | |||
| 47 | #ifndef CONFIG_BT_CMTP_DEBUG | ||
| 48 | #undef BT_DBG | ||
| 49 | #define BT_DBG(D...) | ||
| 50 | #endif | ||
| 51 | |||
| 52 | static int cmtp_sock_release(struct socket *sock) | ||
| 53 | { | ||
| 54 | struct sock *sk = sock->sk; | ||
| 55 | |||
| 56 | BT_DBG("sock %p sk %p", sock, sk); | ||
| 57 | |||
| 58 | if (!sk) | ||
| 59 | return 0; | ||
| 60 | |||
| 61 | sock_orphan(sk); | ||
| 62 | sock_put(sk); | ||
| 63 | |||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
| 68 | { | ||
| 69 | struct cmtp_connadd_req ca; | ||
| 70 | struct cmtp_conndel_req cd; | ||
| 71 | struct cmtp_connlist_req cl; | ||
| 72 | struct cmtp_conninfo ci; | ||
| 73 | struct socket *nsock; | ||
| 74 | void __user *argp = (void __user *)arg; | ||
| 75 | int err; | ||
| 76 | |||
| 77 | BT_DBG("cmd %x arg %lx", cmd, arg); | ||
| 78 | |||
| 79 | switch (cmd) { | ||
| 80 | case CMTPCONNADD: | ||
| 81 | if (!capable(CAP_NET_ADMIN)) | ||
| 82 | return -EACCES; | ||
| 83 | |||
| 84 | if (copy_from_user(&ca, argp, sizeof(ca))) | ||
| 85 | return -EFAULT; | ||
| 86 | |||
| 87 | nsock = sockfd_lookup(ca.sock, &err); | ||
| 88 | if (!nsock) | ||
| 89 | return err; | ||
| 90 | |||
| 91 | if (nsock->sk->sk_state != BT_CONNECTED) { | ||
| 92 | fput(nsock->file); | ||
| 93 | return -EBADFD; | ||
| 94 | } | ||
| 95 | |||
| 96 | err = cmtp_add_connection(&ca, nsock); | ||
| 97 | if (!err) { | ||
| 98 | if (copy_to_user(argp, &ca, sizeof(ca))) | ||
| 99 | err = -EFAULT; | ||
| 100 | } else | ||
| 101 | fput(nsock->file); | ||
| 102 | |||
| 103 | return err; | ||
| 104 | |||
| 105 | case CMTPCONNDEL: | ||
| 106 | if (!capable(CAP_NET_ADMIN)) | ||
| 107 | return -EACCES; | ||
| 108 | |||
| 109 | if (copy_from_user(&cd, argp, sizeof(cd))) | ||
| 110 | return -EFAULT; | ||
| 111 | |||
| 112 | return cmtp_del_connection(&cd); | ||
| 113 | |||
| 114 | case CMTPGETCONNLIST: | ||
| 115 | if (copy_from_user(&cl, argp, sizeof(cl))) | ||
| 116 | return -EFAULT; | ||
| 117 | |||
| 118 | if (cl.cnum <= 0) | ||
| 119 | return -EINVAL; | ||
| 120 | |||
| 121 | err = cmtp_get_connlist(&cl); | ||
| 122 | if (!err && copy_to_user(argp, &cl, sizeof(cl))) | ||
| 123 | return -EFAULT; | ||
| 124 | |||
| 125 | return err; | ||
| 126 | |||
| 127 | case CMTPGETCONNINFO: | ||
| 128 | if (copy_from_user(&ci, argp, sizeof(ci))) | ||
| 129 | return -EFAULT; | ||
| 130 | |||
| 131 | err = cmtp_get_conninfo(&ci); | ||
| 132 | if (!err && copy_to_user(argp, &ci, sizeof(ci))) | ||
| 133 | return -EFAULT; | ||
| 134 | |||
| 135 | return err; | ||
| 136 | } | ||
| 137 | |||
| 138 | return -EINVAL; | ||
| 139 | } | ||
| 140 | |||
| 141 | static struct proto_ops cmtp_sock_ops = { | ||
| 142 | .family = PF_BLUETOOTH, | ||
| 143 | .owner = THIS_MODULE, | ||
| 144 | .release = cmtp_sock_release, | ||
| 145 | .ioctl = cmtp_sock_ioctl, | ||
| 146 | .bind = sock_no_bind, | ||
| 147 | .getname = sock_no_getname, | ||
| 148 | .sendmsg = sock_no_sendmsg, | ||
| 149 | .recvmsg = sock_no_recvmsg, | ||
| 150 | .poll = sock_no_poll, | ||
| 151 | .listen = sock_no_listen, | ||
| 152 | .shutdown = sock_no_shutdown, | ||
| 153 | .setsockopt = sock_no_setsockopt, | ||
| 154 | .getsockopt = sock_no_getsockopt, | ||
| 155 | .connect = sock_no_connect, | ||
| 156 | .socketpair = sock_no_socketpair, | ||
| 157 | .accept = sock_no_accept, | ||
| 158 | .mmap = sock_no_mmap | ||
| 159 | }; | ||
| 160 | |||
| 161 | static struct proto cmtp_proto = { | ||
| 162 | .name = "CMTP", | ||
| 163 | .owner = THIS_MODULE, | ||
| 164 | .obj_size = sizeof(struct bt_sock) | ||
| 165 | }; | ||
| 166 | |||
| 167 | static int cmtp_sock_create(struct socket *sock, int protocol) | ||
| 168 | { | ||
| 169 | struct sock *sk; | ||
| 170 | |||
| 171 | BT_DBG("sock %p", sock); | ||
| 172 | |||
| 173 | if (sock->type != SOCK_RAW) | ||
| 174 | return -ESOCKTNOSUPPORT; | ||
| 175 | |||
| 176 | sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, &cmtp_proto, 1); | ||
| 177 | if (!sk) | ||
| 178 | return -ENOMEM; | ||
| 179 | |||
| 180 | sock_init_data(sock, sk); | ||
| 181 | |||
| 182 | sock->ops = &cmtp_sock_ops; | ||
| 183 | |||
| 184 | sock->state = SS_UNCONNECTED; | ||
| 185 | |||
| 186 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
| 187 | |||
| 188 | sk->sk_protocol = protocol; | ||
| 189 | sk->sk_state = BT_OPEN; | ||
| 190 | |||
| 191 | return 0; | ||
| 192 | } | ||
| 193 | |||
| 194 | static struct net_proto_family cmtp_sock_family_ops = { | ||
| 195 | .family = PF_BLUETOOTH, | ||
| 196 | .owner = THIS_MODULE, | ||
| 197 | .create = cmtp_sock_create | ||
| 198 | }; | ||
| 199 | |||
| 200 | int cmtp_init_sockets(void) | ||
| 201 | { | ||
| 202 | int err; | ||
| 203 | |||
| 204 | err = proto_register(&cmtp_proto, 0); | ||
| 205 | if (err < 0) | ||
| 206 | return err; | ||
| 207 | |||
| 208 | err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops); | ||
| 209 | if (err < 0) | ||
| 210 | goto error; | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | |||
| 214 | error: | ||
| 215 | BT_ERR("Can't register CMTP socket"); | ||
| 216 | proto_unregister(&cmtp_proto); | ||
| 217 | return err; | ||
| 218 | } | ||
| 219 | |||
| 220 | void cmtp_cleanup_sockets(void) | ||
| 221 | { | ||
| 222 | if (bt_sock_unregister(BTPROTO_CMTP) < 0) | ||
| 223 | BT_ERR("Can't unregister CMTP socket"); | ||
| 224 | |||
| 225 | proto_unregister(&cmtp_proto); | ||
| 226 | } | ||
