From bc085054220d80e9308fe5f4c2260e7ccc06e7e3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 2 May 2004 03:36:18 +0000 Subject: Add Bluetooth backend for CUPS --- Makefile.am | 2 +- acinclude.m4 | 65 +++++++++--- configure.in | 4 +- cups/Makefile.am | 16 +++ cups/hcrp.c | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ cups/main.c | 188 +++++++++++++++++++++++++++++++++ cups/sdp.c | 155 +++++++++++++++++++++++++++ cups/spp.c | 103 ++++++++++++++++++ 8 files changed, 834 insertions(+), 15 deletions(-) create mode 100644 cups/Makefile.am create mode 100644 cups/hcrp.c create mode 100644 cups/main.c create mode 100644 cups/sdp.c create mode 100644 cups/spp.c diff --git a/Makefile.am b/Makefile.am index d20a2110..1171035e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ # $Id$ # -SUBDIRS := hcid tools rfcomm sdpd dund pand hidd test scripts pcmcia +SUBDIRS := hcid tools rfcomm sdpd dund pand hidd cups test scripts pcmcia DISTCLEANFILES = conftest.c conftest diff --git a/acinclude.m4 b/acinclude.m4 index dc885de8..5004e983 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -15,14 +15,18 @@ AC_DEFUN(AC_PREFIX_BLUEZ, [ if test "$mandir" = '${prefix}/man'; then AC_SUBST([mandir], ['${prefix}/share/man']) fi + + bluez_prefix="$ac_default_prefix" + else + bluez_prefix="$prefix" fi ]) AC_DEFUN(AC_PATH_BLUEZ, [ AC_ARG_WITH(bluez, [ --with-bluez=DIR BlueZ library is installed in DIR], [ if (test "$withval" = "yes"); then - bluez_includes=/usr/include - bluez_libraries=/usr/lib + bluez_includes=$bluez_prefix/include + bluez_libraries=$bluez_prefix/lib else bluez_includes=$withval/include bluez_libraries=$withval/lib @@ -62,15 +66,14 @@ AC_DEFUN(AC_PATH_BLUEZ, [ ]) AC_DEFUN(AC_PATH_DBUS, [ - AC_ARG_ENABLE(dbus, [ --enable-dbus enable D-BUS support], - dbus_enable=$enableval, - dbus_enable=no - ) + AC_ARG_ENABLE(dbus, [ --enable-dbus enable D-BUS support], [ + dbus_enable=$enableval + ]) AC_ARG_WITH(dbus, [ --with-dbus=DIR D-BUS library is installed in DIR], [ if (test "$withval" = "yes"); then - dbus_includes=/usr/include - dbus_libraries=/usr/lib + dbus_includes=$bluez_prefix/include + dbus_libraries=$bluez_prefix/lib else dbus_includes=$withval/include dbus_libraries=$withval/lib @@ -86,7 +89,7 @@ AC_DEFUN(AC_PATH_DBUS, [ if test -n "$dbus_includes"; then CFLAGS="$CFLAGS -I$dbus_includes -I$dbus_includes/dbus-1.0" else - CFLAGS="$CFLAGS -I/usr/include/dbus-1.0" + CFLAGS="$CFLAGS -I$bluez_prefix/include/dbus-1.0 -I/usr/include/dbus-1.0" fi CFLAGS="$CFLAGS -DDBUS_API_SUBJECT_TO_CHANGE" @@ -95,7 +98,7 @@ AC_DEFUN(AC_PATH_DBUS, [ CFLAGS="$CFLAGS -I$dbus_libraries/dbus-1.0/include" LDFLAGS="$LDFLAGS -L$dbus_libraries" else - CFLAGS="$CFLAGS -I/usr/lib/dbus-1.0/include" + CFLAGS="$CFLAGS -I$bluez_prefix/include/dbus-1.0 -I/usr/lib/dbus-1.0/include" fi if test "$dbus_enable" = "yes"; then @@ -111,7 +114,7 @@ AC_DEFUN(AC_PATH_DBUS, [ if test -n "$dbus_includes"; then DBUS_INCLUDES="-I$dbus_includes -I$dbus_includes/dbus-1.0" else - DBUS_INCLUDES="-I/usr/include/dbus-1.0" + DBUS_INCLUDES="-I$bluez_prefix/include/dbus-1.0 -I/usr/include/dbus-1.0" fi LDFLAGS=$ac_save_LDFLAGS @@ -120,7 +123,7 @@ AC_DEFUN(AC_PATH_DBUS, [ DBUS_LDFLAGS="-L$dbus_libraries" DBUS_LIBS="-L$dbus_libraries $DBUS_LIBS" else - DBUS_INCLUDES="$DBUS_INCLUDES -I/usr/lib/dbus-1.0/include" + DBUS_INCLUDES="$DBUS_INCLUDES -I$bluez_prefix/include/dbus-1.0 -I/usr/lib/dbus-1.0/include" fi AC_SUBST(DBUS_INCLUDES) @@ -129,3 +132,41 @@ AC_DEFUN(AC_PATH_DBUS, [ AM_CONDITIONAL(DBUS, test "$dbus_enable" = "yes") ]) + +AC_DEFUN(AC_PATH_CUPS, [ + AC_ARG_ENABLE(cups, [ --enable-cups enable CUPS support], [ + cups_enable=$enableval + cups_prefix=/usr + ]) + + AC_ARG_WITH(cups, [ --with-cups=DIR CUPS is installed in DIR], [ + if (test "$withval" = "yes"); then + cups_prefix=/usr + else + cups_prefix=$withval + fi + cups_enable=yes + ]) + + CUPS_BACKEND_DIR="" + + if test "$cups_enable" = "yes"; then + AC_MSG_CHECKING(for CUPS backend directory) + + if (test -d "$cups_prefix/lib/cups/backend"); then + CUPS_BACKEND_DIR="$cups_prefix/lib/cups/backend" + else + cups_enable=no + fi + + if test "$cups_enable" = "yes"; then + AC_MSG_RESULT($CUPS_BACKEND_DIR) + else + AC_MSG_RESULT($cups_enable) + fi + fi + + AC_SUBST(CUPS_BACKEND_DIR) + + AM_CONDITIONAL(CUPS, test "$cups_enable" = "yes") +]) diff --git a/configure.in b/configure.in index 58b20bf1..fb6db57d 100644 --- a/configure.in +++ b/configure.in @@ -22,7 +22,7 @@ AC_PROG_YACC AM_PROG_LEX AC_PATH_BLUEZ - AC_PATH_DBUS +AC_PATH_CUPS -AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile dund/Makefile pand/Makefile hidd/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) +AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile dund/Makefile pand/Makefile hidd/Makefile cups/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) diff --git a/cups/Makefile.am b/cups/Makefile.am new file mode 100644 index 00000000..2e4ba707 --- /dev/null +++ b/cups/Makefile.am @@ -0,0 +1,16 @@ +# +# $Id$ +# + +noinst_PROGRAMS = bluetooth + +bluetooth_SOURCES = main.c sdp.c spp.c hcrp.c + +LDFLAGS = @BLUEZ_LIBS@ + +INCLUDES = @BLUEZ_INCLUDES@ + +if CUPS +install-data-local: bluetooth + $(INSTALL) -D -m 755 $(srcdir)/bluetooth $(DESTDIR)@CUPS_BACKEND_DIR@/bluetooth +endif diff --git a/cups/hcrp.c b/cups/hcrp.c new file mode 100644 index 00000000..f69800a7 --- /dev/null +++ b/cups/hcrp.c @@ -0,0 +1,316 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2004 Marcel Holtmann + * + * + * 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. + * + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define HCRP_PDU_CREDIT_GRANT 0x0001 +#define HCRP_PDU_CREDIT_REQUEST 0x0002 +#define HCRP_PDU_GET_LPT_STATUS 0x0005 + +#define HCRP_STATUS_FEATURE_UNSUPPORTED 0x0000 +#define HCRP_STATUS_SUCCESS 0x0001 +#define HCRP_STATUS_CREDIT_SYNC_ERROR 0x0002 +#define HCRP_STATUS_GENERIC_FAILURE 0xffff + +struct hcrp_pdu_hdr { + uint16_t pid; + uint16_t tid; + uint16_t plen; +} __attribute__ ((packed)); +#define HCRP_PDU_HDR_SIZE 6 + +struct hcrp_credit_grant_cp { + uint32_t credit; +} __attribute__ ((packed)); +#define HCRP_CREDIT_GRANT_CP_SIZE 4 + +struct hcrp_credit_grant_rp { + uint16_t status; +} __attribute__ ((packed)); +#define HCRP_CREDIT_GRANT_RP_SIZE 2 + +struct hcrp_credit_request_rp { + uint16_t status; + uint32_t credit; +} __attribute__ ((packed)); +#define HCRP_CREDIT_REQUEST_RP_SIZE 6 + +struct hcrp_get_lpt_status_rp { + uint16_t status; + uint8_t lpt_status; +} __attribute__ ((packed)); +#define HCRP_GET_LPT_STATUS_RP_SIZE 3 + +static int hcrp_credit_grant(int sk, uint16_t tid, uint32_t credit) +{ + struct hcrp_pdu_hdr hdr; + struct hcrp_credit_grant_cp cp; + struct hcrp_credit_grant_rp rp; + unsigned char buf[128]; + int len; + + hdr.pid = htons(HCRP_PDU_CREDIT_GRANT); + hdr.tid = htons(tid); + hdr.plen = htons(HCRP_CREDIT_GRANT_CP_SIZE); + cp.credit = credit; + memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE); + memcpy(buf + HCRP_PDU_HDR_SIZE, &cp, HCRP_CREDIT_GRANT_CP_SIZE); + write(sk, buf, HCRP_PDU_HDR_SIZE + HCRP_CREDIT_GRANT_CP_SIZE); + + len = read(sk, buf, sizeof(buf)); + memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE); + memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_GRANT_RP_SIZE); + + if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) { + errno = EIO; + return -1; + } + + return 0; +} + +static int hcrp_credit_request(int sk, uint16_t tid, uint32_t *credit) +{ + struct hcrp_pdu_hdr hdr; + struct hcrp_credit_request_rp rp; + unsigned char buf[128]; + int len; + + hdr.pid = htons(HCRP_PDU_CREDIT_REQUEST); + hdr.tid = htons(tid); + hdr.plen = htons(0); + memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE); + write(sk, buf, HCRP_PDU_HDR_SIZE); + + len = read(sk, buf, sizeof(buf)); + memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE); + memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_REQUEST_RP_SIZE); + + if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) { + errno = EIO; + return -1; + } + + if (credit) + *credit = ntohl(rp.credit); + + return 0; +} + +static int hcrp_get_lpt_status(int sk, uint16_t tid, uint8_t *lpt_status) +{ + struct hcrp_pdu_hdr hdr; + struct hcrp_get_lpt_status_rp rp; + unsigned char buf[128]; + int len; + + hdr.pid = htons(HCRP_PDU_GET_LPT_STATUS); + hdr.tid = htons(tid); + hdr.plen = htons(0); + memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE); + write(sk, buf, HCRP_PDU_HDR_SIZE); + + len = read(sk, buf, sizeof(buf)); + memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE); + memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_GET_LPT_STATUS_RP_SIZE); + + if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) { + errno = EIO; + return -1; + } + + if (lpt_status) + *lpt_status = rp.lpt_status; + + return 0; +} + +static inline int hcrp_get_next_tid(int tid) +{ + if (tid > 0xf000) + return 0; + else + return tid + 1; +} + +int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies) +{ + struct sockaddr_l2 addr; + struct l2cap_options opts; + unsigned char buf[2048]; + int i, size, ctrl_sk, data_sk, mtu, count, len, timeout = 0; + uint8_t status; + uint16_t tid = 0; + uint32_t tmp, credit = 0; + + if ((ctrl_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) { + perror("ERROR: Can't create socket"); + return 1; + } + + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, src); + addr.l2_psm = 0; + + if (bind(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("ERROR: Can't bind socket"); + close(ctrl_sk); + return 1; + } + + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, dst); + addr.l2_psm = htobs(ctrl_psm); + + if (connect(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("ERROR: Can't connect to device"); + close(ctrl_sk); + return 1; + } + + if ((data_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) { + perror("ERROR: Can't create socket"); + close(ctrl_sk); + return 1; + } + + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, src); + addr.l2_psm = 0; + + if (bind(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("ERROR: Can't bind socket"); + close(data_sk); + close(ctrl_sk); + return 1; + } + + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, dst); + addr.l2_psm = htobs(data_psm); + + if (connect(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("ERROR: Can't connect to device"); + close(data_sk); + close(ctrl_sk); + return 1; + } + + size = sizeof(opts); + + if (getsockopt(data_sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) { + perror("ERROR: Can't get socket options"); + close(data_sk); + close(ctrl_sk); + return 1; + } + + mtu = opts.omtu; + + /* Ignore SIGTERM signals if printing from stdin */ + if (fd == 0) { +#ifdef HAVE_SIGSET + sigset(SIGTERM, SIG_IGN); +#elif defined(HAVE_SIGACTION) + memset(&action, 0, sizeof(action)); + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_IGN; + sigaction(SIGTERM, &action, NULL); +#else + signal(SIGTERM, SIG_IGN); +#endif /* HAVE_SIGSET */ + } + + tid = hcrp_get_next_tid(tid); + if (hcrp_credit_grant(ctrl_sk, tid, 0) < 0) { + fprintf(stderr, "ERROR: Can't grant initial credits\n"); + close(data_sk); + close(ctrl_sk); + return 1; + } + + for (i = 0; i < copies; i++) { + + if (fd != 0) { + fprintf(stderr, "PAGE: 1 1\n"); + lseek(fd, 0, SEEK_SET); + } + + while (1) { + if (credit < mtu) { + tid = hcrp_get_next_tid(tid); + if (!hcrp_credit_request(ctrl_sk, tid, &tmp)) { + credit += tmp; + timeout = 0; + } + } + + if (!credit) { + if (timeout++ > 300) { + tid = hcrp_get_next_tid(tid); + if (!hcrp_get_lpt_status(ctrl_sk, tid, &status)) + fprintf(stderr, "ERROR: LPT status 0x%02x\n", status); + break; + } + + sleep(1); + continue; + } + + count = read(fd, buf, (credit > mtu) ? mtu : credit); + if (count <= 0) + break; + + len = write(data_sk, buf, count); + if (len != count) + fprintf(stderr, "ERROR: Can't send complete data\n"); + + credit -= len; + } + + } + + close(data_sk); + close(ctrl_sk); + + return 0; +} diff --git a/cups/main.c b/cups/main.c new file mode 100644 index 00000000..352ea4ea --- /dev/null +++ b/cups/main.c @@ -0,0 +1,188 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2004 Marcel Holtmann + * + * + * 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. + * + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel); +int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm); + +int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies); +int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies); + +static void list_devices(void) +{ +} + +/* + * Usage: printer-uri job-id user title copies options [file] + * + */ + +int main(int argc, char *argv[]) +{ + sdp_session_t *sdp; + bdaddr_t bdaddr; + unsigned short ctrl_psm, data_psm; + uint8_t channel, b[6]; + char *ptr, str[3], device[18], service[12]; + int i, err, fd, copies, proto; + + /* Make sure status messages are not buffered */ + setbuf(stderr, NULL); + + /* Ignore SIGPIPE signals */ +#ifdef HAVE_SIGSET + sigset(SIGPIPE, SIG_IGN); +#elif defined(HAVE_SIGACTION) + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); +#else + signal(SIGPIPE, SIG_IGN); +#endif /* HAVE_SIGSET */ + + if (argc == 1) { + list_devices(); + return 0; + } + + if (argc < 6 || argc > 7) { + fprintf(stderr, "Usage: bluetooth job-id user title copies options [file]\n"); + return 1; + } + + if (argc == 6) { + fd = 0; + copies = 1; + } else { + if ((fd = open(argv[6], O_RDONLY)) < 0) { + perror("ERROR: Unable to open print file"); + return 1; + } + copies = atoi(argv[4]); + } + + if (strncasecmp(argv[0], "bluetooth://", 12)) { + fprintf(stderr, "ERROR: No device URI found\n"); + return 1; + } + + ptr = argv[0] + 12; + for (i = 0; i < 6; i++) { + strncpy(str, ptr, 2); + b[i] = (uint8_t) strtol(str, NULL, 16); + ptr += 2; + } + sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + b[0], b[1], b[2], b[3], b[4], b[5]); + + str2ba(device, &bdaddr); + + ptr = strchr(ptr, '/'); + if (ptr) { + strncpy(service, ptr + 1, 12); + + if (!strncasecmp(ptr + 1, "spp", 3)) + proto = 1; + else if (!strncasecmp(ptr + 1, "hcrp", 4)) + proto = 2; + else + proto = 0; + } else { + strcpy(service, "auto"); + proto = 0; + } + + fprintf(stderr, "DEBUG: %s device %s service %s fd %d copies %d\n", + argv[0], device, service, fd, copies); + + sdp = sdp_connect(BDADDR_ANY, &bdaddr, 0 /*SDP_RETRY_IF_BUSY*/); + if (!sdp) { + fprintf(stderr, "ERROR: Can't open Bluetooth connection\n"); + return 1; + } + + switch (proto) { + case 1: + err = sdp_search_spp(sdp, &channel); + break; + case 2: + err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm); + break; + default: + proto = 2; + err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm); + if (err) { + proto = 1; + err = sdp_search_spp(sdp, &channel); + } + break; + } + + sdp_close(sdp); + + if (err) { + fprintf(stderr, "ERROR: Can't get service information\n"); + return 1; + } + + switch (proto) { + case 1: + err = spp_print(BDADDR_ANY, &bdaddr, channel, fd, copies); + break; + case 2: + err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm, fd, copies); + break; + default: + err = 1; + fprintf(stderr, "ERROR: Unsupported protocol\n"); + break; + } + + if (fd != 0) + close(fd); + + if (!err) + fprintf(stderr, "INFO: Ready to print\n"); + + return err; +} diff --git a/cups/sdp.c b/cups/sdp.c new file mode 100644 index 00000000..7491ab1f --- /dev/null +++ b/cups/sdp.c @@ -0,0 +1,155 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2004 Marcel Holtmann + * + * + * 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. + * + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define HCRP_CTRL_UUID 0x0012 +#define HCRP_DATA_UUID 0x0014 + +#define HCR_PROFILE_ID 0x1125 +#define HCR_PRINT_SVCLASS_ID 0x1126 + +#define SDP_ATTR_ADD_PROTO_DESC_LIST 0x000d + +static int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **pap) +{ + sdp_data_t *pdlist, *curr; + sdp_list_t *ap = 0; + + pdlist = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST); + if (pdlist == NULL) { + errno = ENODATA; + return -1; + } + + pdlist = pdlist->val.dataseq; + + for (; pdlist; pdlist = pdlist->next) { + sdp_list_t *pds = 0; + for (curr = pdlist->val.dataseq; curr; curr = curr->next) + pds = sdp_list_append(pds, curr->val.dataseq); + ap = sdp_list_append(ap, pds); + } + + *pap = ap; + + return 0; +} + +int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm) +{ + sdp_list_t *srch, *attrs, *rsp; + uuid_t svclass; + uint16_t attr1, attr2; + int err; + + if (!sdp) + return -1; + + sdp_uuid16_create(&svclass, HCR_PRINT_SVCLASS_ID); + srch = sdp_list_append(NULL, &svclass); + + attr1 = SDP_ATTR_PROTO_DESC_LIST; + attrs = sdp_list_append(NULL, &attr1); + attr2 = SDP_ATTR_ADD_PROTO_DESC_LIST; + attrs = sdp_list_append(attrs, &attr2); + + err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); + if (err) + return -1; + + for (; rsp; rsp = rsp->next) { + sdp_record_t *rec = (sdp_record_t *) rsp->data; + sdp_list_t *protos; + + if (!sdp_get_access_protos(rec, &protos)) { + unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID); + if (psm > 0) { + *ctrl_psm = psm; + } + } + + if (!sdp_get_add_access_protos(rec, &protos)) { + unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID); + if (psm > 0 && *ctrl_psm > 0) { + *data_psm = psm; + return 0; + } + } + } + + return -1; +} + +int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel) +{ + sdp_list_t *srch, *attrs, *rsp; + uuid_t svclass; + uint16_t attr; + int err; + + if (!sdp) + return -1; + + sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID); + srch = sdp_list_append(NULL, &svclass); + + attr = SDP_ATTR_PROTO_DESC_LIST; + attrs = sdp_list_append(NULL, &attr); + + err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); + if (err) + return -1; + + for (; rsp; rsp = rsp->next) { + sdp_record_t *rec = (sdp_record_t *) rsp->data; + sdp_list_t *protos; + + if (!sdp_get_access_protos(rec, &protos)) { + uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID); + if (ch > 0) { + *channel = ch; + return 0; + } + } + } + + return -1; +} diff --git a/cups/spp.c b/cups/spp.c new file mode 100644 index 00000000..ec8fcdd5 --- /dev/null +++ b/cups/spp.c @@ -0,0 +1,103 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2004 Marcel Holtmann + * + * + * 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. + * + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies) +{ + struct sockaddr_rc addr; + unsigned char buf[2048]; + int i, sk, len; + + if ((sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { + perror("ERROR: Can't create socket"); + return 1; + } + + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, src); + addr.rc_channel = 0; + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("ERROR: Can't bind socket"); + close(sk); + return 1; + } + + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, dst); + addr.rc_channel = channel; + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("ERROR: Can't connect to device"); + close(sk); + return 1; + } + + /* Ignore SIGTERM signals if printing from stdin */ + if (fd == 0) { +#ifdef HAVE_SIGSET + sigset(SIGTERM, SIG_IGN); +#elif defined(HAVE_SIGACTION) + memset(&action, 0, sizeof(action)); + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_IGN; + sigaction(SIGTERM, &action, NULL); +#else + signal(SIGTERM, SIG_IGN); +#endif /* HAVE_SIGSET */ + } + + for (i = 0; i < copies; i++) { + + if (fd != 0) { + fprintf(stderr, "PAGE: 1 1\n"); + lseek(fd, 0, SEEK_SET); + } + + while ((len = read(fd, buf, sizeof(buf))) > 0) { + write(sk, buf, len); + } + + } + + close(sk); + + return 0; +} -- cgit