Logo Search packages:      
Sourcecode: affix-kernel version File versions  Download package

btuart.c

/* 
   Affix - Bluetooth Protocol Stack for Linux
   Copyright (C) 2001 Nokia Corporation
   Original Author: Dmitry Kasatkin <dmitry.kasatkin@nokia.com>

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2 of the License, or (at your
   option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

/* 
   $Id: btuart.c,v 1.129 2004/02/20 13:46:54 kassatki Exp $

   BTUART - physical protocol layer for UART based cards

   Fixes:   Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
                Imre Deak <ext-imre.deak@nokia.com>
            Shrirang Bhagwat <shrirangb@aftek.com>
*/


/* The following prevents "kernel_version" from being set in this file. */
#define __NO_VERSION__

#include <linux/config.h>

/* Module related headers, non-module drivers should not include */
#include <linux/module.h>

/* Standard driver includes */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/termios.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>

#include <linux/skbuff.h>

/* Local Includes */
#define     FILEBIT     DBDRV

#include <affix/btdebug.h>
#include <affix/hci.h>
#include <affix/uart.h>

#if defined(CONFIG_AFFIX_UART_BCSP)
#include "btuart_bcsp.h"
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#include <asm/io.h>
#include <linux/serial_reg.h>
#define AFFIX_16C950_BUG
#endif

void btuart_rx_task(btuart_t *btuart);

int  btuartld_open(struct tty_struct *tty);
void btuartld_close(struct tty_struct *tty);
int  btuartld_ioctl(struct tty_struct *, void *, int, void *);
int  btuartld_receive_room(struct tty_struct *tty);
void btuartld_write_wakeup(struct tty_struct *tty);
void btuartld_receive_buf(struct tty_struct *, const unsigned char *, char *, int);


struct tty_ldisc  btuart_ldisc;

btuart_proto_t    btuart_protos[] = {
#if defined(CONFIG_AFFIX_UART_TLP)
{
      proto:                  HCI_UART_TLP,
      hard_hdr_len:           TLP_HDR_LEN,
      enqueue:          btuart_enqueue_tlp,
      dequeue:          btuart_dequeue_tlp,
      init:             btuart_init_tlp,
      uninit:                 btuart_uninit_tlp,
      recv_buf:         btuart_recv_buf_tlp,
},
#endif
#if defined(CONFIG_AFFIX_UART_BCSP)
{
      /* BCSP protocol descriptor */
      proto:                  HCI_UART_BCSP,
      hard_hdr_len:           16,
      enqueue:          bcsp_enqueue,
      dequeue:          bcsp_dequeue,
      init:             bcsp_open,
      uninit:                 bcsp_close,
      recv_buf:         (void*)bcsp_recv,
},
#endif
#if defined(CONFIG_AFFIX_UART_H4)
{
      /* last - default descriptor */
      proto:                  HCI_UART_H4,
      hard_hdr_len:           1,
      enqueue:          btuart_enqueue_h4,
      dequeue:          btuart_dequeue_h4,
      init:             btuart_init_h4,
      uninit:                 btuart_uninit_h4,
      recv_buf:         btuart_recv_buf_h4,
}
#endif
};

#define NOF_UART_DEVS (sizeof(btuart_protos) / sizeof(btuart_protos[0]))

static btuart_proto_t *get_by_proto(int proto)
{
      int   i;
      
      for (i = 0; i < NOF_UART_DEVS; i++) {
            if (proto == btuart_protos[i].proto)
                  return &btuart_protos[i];
      }
      return NULL;
}


btlist_head_t     btuarts;

/*************************** FUNCTION DEFINITION SECTION **********************/

btuart_t *__btuart_lookup_device(char *name)
{
      btuart_t    *btuart;

      btl_for_each (btuart, btuarts) {
            if (strcmp(btuart->uart.name, name) == 0)
                  break;
      }
      return btuart;
}

btuart_t *btuart_lookup_device(char *name)
{
      btuart_t    *btuart;
      unsigned long     flags;

      read_lock_irqsave(&btuarts.lock, flags);
      btuart = __btuart_lookup_device(name);
      read_unlock_irqrestore(&btuarts.lock, flags);

      return btuart;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
btuart_t *__btuart_lookup_kdev(kdev_t kdev)
{
      btuart_t    *btuart;

      btl_for_each (btuart, btuarts)
            if (kdev_same(btuart->kdev, kdev))
                  break;
      return btuart;
}

btuart_t *btuart_lookup_kdev(kdev_t kdev)
{
      btuart_t    *btuart;
      unsigned long     flags;

      read_lock_irqsave(&btuarts.lock, flags);
      btuart = __btuart_lookup_kdev(kdev);
      read_unlock_irqrestore(&btuarts.lock, flags);

      return btuart;
}
#else
btuart_t *__btuart_lookup_dev(dev_t dev)
{
      btuart_t    *btuart;

      btl_for_each (btuart, btuarts)
            if (btuart->dev == dev)
                  break;
      return btuart;
}

btuart_t *btuart_lookup_dev(struct tty_struct *tty)
{
      btuart_t    *btuart;
      unsigned long     flags;
      dev_t       device;

      device = MKDEV(tty->driver->major, tty->driver->minor_start + tty->index);

      read_lock_irqsave(&btuarts.lock, flags);
      btuart = __btuart_lookup_dev(device);
      read_unlock_irqrestore(&btuarts.lock, flags);

      return btuart;
}
#endif


btuart_t *btuart_create(void)
{
      btuart_t    *btuart;
      unsigned long     flags;
      
      btuart = kmalloc(sizeof(*btuart), GFP_KERNEL);
      if (btuart == NULL)
            return NULL;
      memset(btuart, 0, sizeof(*btuart));

#ifdef CONFIG_AFFIX_UART_NEW_BH
      BTINFO("Using BT Inbuffers [%d Bytes]\n", BT_INBUFFER_SIZE);
      btuart->hci_data.head = &btuart->hci_data.data[0];
      btuart->hci_data.tail = &btuart->hci_data.data[BT_INBUFFER_SIZE-1];
      btuart->hci_data.put = btuart->hci_data.head;
      btuart->hci_data.get = btuart->hci_data.head;;
#endif

      rwlock_init(&btuart->lock);
      spin_lock_init(&btuart->xmit_lock);
      skb_queue_head_init(&btuart->tx_q);

      write_lock_irqsave(&btuarts.lock, flags);
      __btl_add_tail(&btuarts, btuart);
      write_unlock_irqrestore(&btuarts.lock, flags);

      return btuart;
}

void btuart_destroy(btuart_t *btuart)
{
      unsigned long     flags;

      write_lock_irqsave(&btuarts.lock, flags);
      __btl_unlink(&btuarts, btuart);
      kfree(btuart);
      write_unlock_irqrestore(&btuarts.lock, flags);
}


/* ***************** UART common ****************************** */

struct file *kdev_open(char *name)
{
      struct file *file;

      DBFENTER;
      DBPRT("Opening device, path: %s\n", name);
      file = filp_open(name, 0, 0);
      if (IS_ERR(file)) {
            BTERROR("Unable to open divice: %s\n", name);
            return NULL;
      }
      DBFEXIT;
      return file;
}

int kdev_close(struct file *file)
{
      int   err;
      DBFENTER;
      err = filp_close(file, NULL);
      DBFEXIT;
      return err;
}

int kdev_ioctl(struct file *filp, int cmd, void *arg)
{
      int   err = -1;
      
      if (filp->f_op && filp->f_op->ioctl) {
            mm_segment_t      old_fs;
            old_fs = get_fs(); set_fs(KERNEL_DS);
            err = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, (unsigned long)arg);
            set_fs(old_fs);
      }
      return err;
}

/*
#define    B57600 0010001
#define   B115200 0010002
#define   B230400 0010003
#define   B460800 0010004
#define   B500000 0010005
#define   B576000 0010006
#define   B921600 0010007
*/

#ifdef AFFIX_16C950_BUG
/*
 * For the 16C950
 */
void serial_icr_write(btuart_t *btuart, int offset, int  value)
{
      serial_out(btuart, UART_SCR, offset);
      serial_out(btuart, UART_ICR, value);
}

#endif

int init_uart(btuart_t *btuart)
{
      int               err;
      struct termios          term;

      DBFENTER;

      /* get current settings */
      err = kdev_ioctl(btuart->filp, TCGETS, &term);
      if (err)
            goto ioerr;
      //term.c_iflag |= IGNPAR;
      if (btuart->uart.flags & CRTSCTS)
            term.c_cflag |= CRTSCTS;
      else
            term.c_cflag &= ~CRTSCTS;
      err = kdev_ioctl(btuart->filp, TCSETS, &term);
      if (err) {
            BTERROR("Unable to set CRTSCTS: %d\n", err);
            goto ioerr;
      }

      if (btuart->uart.flags & CSTOPB)
            term.c_cflag |= CSTOPB; /* 1 stop bit */
      else
            term.c_cflag &= ~CSTOPB;/* 1.5 stop bits */
      err = kdev_ioctl(btuart->filp, TCSETS, &term);
      if (err) {
            BTERROR("Unable to set CSTOPB: %d\n", err);
            goto ioerr;
      }

      if (btuart->uart.flags & PARENB) {
            term.c_cflag |= PARENB;
            if (btuart->uart.flags & PARODD)
                  term.c_cflag |= PARODD;
            else
                  term.c_cflag &= ~PARODD;
      } else
            term.c_cflag &= ~PARENB;

      err = kdev_ioctl(btuart->filp, TCSETS, &term);
      if (err) {
            BTERROR("Unable to set PARITY: %d\n", err);
            goto ioerr;
      }
      
      DBPRT("Setting speed: %o\n", btuart->uart.speed);
      if (btuart->uart.speed) {
            int   baud = btuart->uart.speed;
#ifdef AFFIX_16C950_BUG
            int   tcr = 0;
            BTINFO("Working around 16C950 bug in 8250.c ...\n");
            if (btuart->ser.type == PORT_16C950 
                        && btuart->ser.baud_base <= 115200) {
                  if (baud <= B115200)
                        tcr = 0;
                  else if (baud <= B230400) {
                        tcr = 0x8;  //230400
                        baud = B115200;
                  } else if (baud <= B460800) {
                        tcr = 0x4;  //460800
                        baud = B115200;
                  } else
                        tcr = 0;
            }
#endif
            term.c_cflag = (term.c_cflag & ~CBAUD) | baud;
            err = kdev_ioctl(btuart->filp, TCSETS, &term);
            if (err) {
                  BTERROR("Unable to set speed: %d\n", err);
                  goto ioerr;
            }
#ifdef AFFIX_16C950_BUG
            if (btuart->ser.type == PORT_16C950)
                  serial_icr_write(btuart, UART_TCR, tcr);
#endif
      }
ioerr:
      DBFEXIT;
      return err;
}

int btuart_xmit_start(btuart_t *btuart)
{
      int   actual;

      DBFENTER;
      for (;;) {
            if (!test_bit(BTUART_RUNNING, &btuart->flags) || !hcidev_present(btuart->hci))
                  return -1;

            if (!btuart->tx_skb) {
                  btuart->tx_skb = btuart->proto.dequeue(btuart);
                  if (!btuart->tx_skb) {
                        //btuart->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
                        clear_bit(TTY_DO_WRITE_WAKEUP, &btuart->tty->flags);
                        break;
                  }
            }
            DBDUMP(btuart->tx_skb->data, btuart->tx_skb->len);
            
            //btuart->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
            set_bit(TTY_DO_WRITE_WAKEUP, &btuart->tty->flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
            actual = btuart->tty->driver.write(btuart->tty, 0, 
                        btuart->tx_skb->data, btuart->tx_skb->len);
#else
            actual = btuart->tty->driver->write(btuart->tty, 0, 
                        btuart->tx_skb->data, btuart->tx_skb->len);
#endif

            DBPRT("packet sent: %d of %d bytes\n", actual, btuart->tx_skb->len);
            skb_pull(btuart->tx_skb, actual);
            if (btuart->tx_skb->len)
                  break;
            dev_kfree_skb_any(btuart->tx_skb);
            btuart->tx_skb = NULL;
      }
      DBFEXIT;
      return 0;
}

void btuart_xmit_wakeup(btuart_t *btuart)
{
      int   err = 0;

      DBFENTER;
      set_bit(BTUART_XMIT_WAKEUP, &btuart->flags);
      while (spin_trylock_bh(&btuart->xmit_lock)) {
            clear_bit(BTUART_XMIT_WAKEUP, &btuart->flags);
            err = btuart_xmit_start(btuart);
            spin_unlock_bh(&btuart->xmit_lock);
            if (err || !test_bit(BTUART_XMIT_WAKEUP, &btuart->flags))
                  break;
      }
      DBFEXIT;
}

int btuart_net_xmit(hci_struct *hci, struct sk_buff *skb)
{
      btuart_t    *btuart = hci->priv;

      DBFENTER;
      if (!test_bit(BTUART_RUNNING, &btuart->flags)) {
            DBPRT("%s: xmit call when iface is down\n", hci->name);
            dev_kfree_skb_any(skb);
            return 0;
      }
      hci->trans_start = jiffies;
      btuart->proto.enqueue(btuart, skb);
      btuart_xmit_wakeup(btuart);
      DBFEXIT;
      return 0;
}


/***********************   Network Interface Subsystem  ********************/

int btuart_open(hci_struct *hci)
{
      int         disc = N_BTUART, err;
      btuart_t    *btuart = hci->priv;

      DBFENTER;
      if (test_and_set_bit(BTUART_RUNNING, &btuart->flags))
            return -EBUSY;

      DBPRT("proto: %d, name: %s\n", btuart->proto.proto, btuart->uart.name);

      if (!btuart->proto.proto) {
            clear_bit(BTUART_RUNNING, &btuart->flags);
            return -EBUSY;
      }

      btuart->filp = kdev_open(btuart->uart.name);
      if (btuart->filp == NULL) {
            clear_bit(BTUART_RUNNING, &btuart->flags);
            return -EBUSY;
      }
      /* get kdev */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
      btuart->kdev = btuart->filp->f_dentry->d_inode->i_rdev;
#else
      btuart->dev = btuart->filp->f_dentry->d_inode->i_rdev;
#endif
      
      err = kdev_ioctl(btuart->filp, TIOCGSERIAL, &btuart->ser);
      if (err)
            goto ioerr;

      err = init_uart(btuart);
      if (err)
            goto ioerr;

      err = kdev_ioctl(btuart->filp, TIOCSETD, &disc);
      if (err)
            goto ioerr;

      /* now we are able to receive data */
      btuart->tx_skb = NULL;
      btuart->rx_skb = NULL;
      btuart->rx_count = 0;

      tasklet_init(&btuart->tx_task, (void*)btuart_xmit_wakeup, (unsigned long)btuart);
#ifdef CONFIG_AFFIX_UART_NEW_BH
      tasklet_init(&btuart->rx_task, (void*)btuart_rx_task, (unsigned long)btuart);
#endif

      /* protocol specific init */
      if (btuart->proto.init) {
            err = btuart->proto.init(btuart);
            if (err)
                  goto ioerr;
      }

      if (btuart->uart.count)
            (*btuart->uart.count)++;

      hcidev_start_queue(btuart->hci);

      DBFEXIT;
      return 0;
ioerr:
      clear_bit(BTUART_RUNNING, &btuart->flags);
      DBPRT("Unable to set discipline/terminal parameters: err: %d\n", err);
      kdev_close(btuart->filp);
      btuart->filp = NULL;
      return err;
}

int btuart_stop(hci_struct *hci)
{
      btuart_t    *btuart = hci->priv;
      
      DBFENTER;
      if (!test_and_clear_bit(BTUART_RUNNING, &btuart->flags))
            return 0;

      hcidev_stop_queue(btuart->hci);
      write_lock(&btuart->lock);    /* lock */
      tasklet_kill(&btuart->tx_task);
#ifdef CONFIG_AFFIX_UART_NEW_BH
      tasklet_kill(&btuart->rx_task);
#endif
      if (btuart->proto.uninit)
            btuart->proto.uninit(btuart);

      /* cleanup resources */
      if (btuart->tx_skb)
            kfree_skb(btuart->tx_skb);
      if (btuart->rx_skb)
            kfree_skb(btuart->rx_skb);
      skb_queue_purge(&btuart->tx_q);
      
      kdev_close(btuart->filp);
      btuart->filp = NULL;

      if (btuart->uart.count)
            (*btuart->uart.count)--;

      write_unlock(&btuart->lock);  /* unlock */
      DBFEXIT;
      return 0;
}

int btuart_ioctl(hci_struct *hci, int cmd, void *arg)
{
      btuart_t    *btuart = (void*)hci->priv;
      int         err = 0;

      DBFENTER;
      
      if (!btuart)      // sanity check
            return -ENODEV;

      if (_IOC_TYPE(cmd) == 'T') {
            /* terminal ioctl */

            if (!btuart->filp)
                  return -ENOTTY;
            err = btuart->filp->f_op->ioctl(btuart->filp->f_dentry->d_inode, 
                                    btuart->filp, cmd, (unsigned long)arg);
            //err = kdev_ioctl(btuart->filp, cmd, arg);
      } else {
            switch (cmd) {
                  case BTIOC_SETUP_UART:
                  {
                        struct open_uart  *uart = (void*)arg;
                        btuart_proto_t          *proto;

                        btuart->uart.speed = uart->speed;
                        btuart->uart.flags = uart->flags;

                        /* select configuration */
                        proto = get_by_proto(uart->proto);
                        if (!proto)
                              return -EPROTONOSUPPORT;
                        btuart->proto = *proto;
                        btuart->hci->hdrlen = btuart->proto.hard_hdr_len;
                        DBPRT("proto: %p, %d\n", proto, btuart->proto.proto);
                        if (btuart->filp)
                              /* change settings */
                              err = init_uart(btuart);
                  }
                  break;

                  default:
                        return -ENOIOCTLCMD;
            }
      }
      DBFEXIT;
      return err;
}

/* ****************** attachment stuff ***************************** */

int btuart_register_netdev(btuart_t *btuart)
{
      hci_struct  *hci;
      int         err;

      DBFENTER;   
      hci = hcidev_alloc();
      if (hci == NULL)
            return -ENOMEM;

      hci->priv = btuart;     /* set private pointer */
      hci->open = btuart_open;
      hci->close = btuart_stop;
      hci->ioctl = btuart_ioctl;
      hci->send = btuart_net_xmit;
      hci->hdrlen = btuart->proto.hard_hdr_len;
      hci->type = btuart->uart.manfid?HCI_UART_CS:HCI_UART;
      hci->owner = THIS_MODULE;
      btuart->hci = hci;
      err = hcidev_register(hci, &btuart->uart);
      
      DBFEXIT;
      return err;
}

static inline void btuart_unregister_netdev(btuart_t *btuart)
{
      DBFENTER;
      if (btuart->hci) {
            hcidev_unregister(btuart->hci);
            btuart->hci = NULL;
      }
      DBFEXIT;
}


int affix_uart_attach(affix_uart_t *uart)
{
      int               err;
      btuart_t          *btuart;
      btuart_proto_t          *proto;
      
      DBFENTER;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      if (!try_inc_mod_count(uart->owner))
#else
      if (!try_module_get(uart->owner))
#endif
            return -ENODEV;

      btuart = btuart_lookup_device(uart->name);
      if (btuart) {
            err = -EBUSY;
            goto err;
      }

      btuart = btuart_create();
      if (!btuart) {
            err = -ENOMEM;
            goto err;
      }
      /* set uart info */
      btuart->uart = *uart;

      /* select configuration */
      proto = get_by_proto(uart->proto);
      if (!proto)
            btuart->proto.proto = 0;      /* no proto */
      else
            btuart->proto = *proto;

      err = btuart_register_netdev(btuart);
      if (!err)
            return 0;
err:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      if (uart->owner)
            __MOD_DEC_USE_COUNT(uart->owner);
#else
        module_put(uart->owner);
#endif
      return err;
}

int affix_uart_detach(char *name)
{
      btuart_t *btuart;

      DBFENTER;
      btuart = btuart_lookup_device(name);
      if (!btuart)
            return -ENODEV;
      btuart_unregister_netdev(btuart);
      btuart->uart.count = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      if (btuart->uart.owner)
            __MOD_DEC_USE_COUNT(btuart->uart.owner);
#else
        module_put(btuart->uart.owner);
#endif
      btuart_destroy(btuart);
      DBFEXIT;
      return 0;
}

void affix_uart_suspend(char *name)
{
      btuart_t *btuart;

      DBFENTER;
      btuart = btuart_lookup_device(name);
      if (!btuart)
            return;
      if (btuart->hci)
            hcidev_detach(btuart->hci);
      DBFEXIT;
      return;
}

void affix_uart_resume(char *name)
{
      btuart_t *btuart;

      DBFENTER;
      btuart = btuart_lookup_device(name);
      if (!btuart)
            return;
      if (btuart->hci)
            hcidev_attach(btuart->hci);
      DBFEXIT;
      return;
}

struct affix_uart_operations btuart_ops = {
      owner: THIS_MODULE,
      attach: affix_uart_attach,
      detach: affix_uart_detach,
      suspend: affix_uart_suspend,
      resume: affix_uart_resume
};
       
/* ************************  Line Discipline  **************************** */

/*
 * Line discipline section
 */
int btuartld_open(struct tty_struct *tty)
{
      btuart_t    *btuart;
      int         err = -EEXIST;

      DBFENTER;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      MOD_INC_USE_COUNT;
#endif
      /* First make sure we're not already connected. */
      btuart = (btuart_t *) tty->disc_data;
      if (btuart) {
            BTERROR("Already opened\n");
            goto err1;
      }

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      btuart = btuart_lookup_kdev(tty->device);
#else
      btuart = btuart_lookup_dev(tty);
#endif
      if (btuart == NULL) {
            BTERROR("Device not found\n");
            err = -ENODEV;
            goto err1;
      }

      btuart->tty = tty;
      tty->disc_data = btuart;

      /* flush all internal buffers */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      if (tty->driver.flush_buffer)
            tty->driver.flush_buffer(tty);
#else
      if (tty->driver->flush_buffer)
            tty->driver->flush_buffer(tty);
#endif

      if (tty->ldisc.flush_buffer)
            tty->ldisc.flush_buffer(tty);
  
      tty->low_latency = (btuart->uart.flags & AFFIX_UART_LOW)? 1: 0;

      DBFEXIT;
      return 0;

 err1:
      tty->disc_data = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      MOD_DEC_USE_COUNT;
#endif
      return err;
}

void btuartld_close(struct tty_struct *tty)
{
      btuart_t    *btuart = (btuart_t *)tty->disc_data;
      
      DBFENTER;

      if (!btuart)
            return;
      /* Stop tty */
      tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
      tty->disc_data = 0;
      btuart->tty = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
      MOD_DEC_USE_COUNT;
#endif

      DBFEXIT;
}

/*
 * Function btuartld_ioctl (tty, file, cmd, arg)
 */
int  btuartld_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
{
      btuart_t    *btuart = (btuart_t *)tty->disc_data;
      int         err;

      DBFENTER;

      if (!btuart)
            return -ENODEV;

      DBPRT("ioctl, cmd: %#x\n", cmd);
      err = n_tty_ioctl(tty, (struct file *) file, cmd, (unsigned long) arg);

      DBFEXIT;
      return err; 
}


#ifdef CONFIG_AFFIX_UART_NEW_BH
void btuart_rx_task(btuart_t *btuart)
{
      s32 size_end;
      s32 size_start;
      u8* getTemp;

      cli();
      if (btuart->hci_data.get == btuart->hci_data.put) {
            sti();
            return;
      } else if (btuart->hci_data.get > btuart->hci_data.put) {
            /* buffer is wrapped */
            size_end = btuart->hci_data.tail - btuart->hci_data.get + 1;
            size_start = btuart->hci_data.put - btuart->hci_data.head;
            getTemp = btuart->hci_data.get;

            /* Indicate that all data has been fetched (or soon will be) 
             *             by setting get == put. */
            btuart->hci_data.get = btuart->hci_data.put;
            sti();

            btuart->proto.recv_buf(btuart, getTemp, size_end);
            btuart->proto.recv_buf(btuart, btuart->hci_data.head, size_start);
      }
      else {
            /* no wrapped buffer */
            size_end = btuart->hci_data.put - btuart->hci_data.get;
            getTemp = btuart->hci_data.get;
            /* Indicate that all data has been fetched (or soon will be)
             *             by setting get == put. */
            btuart->hci_data.get = btuart->hci_data.put;
            sti();

            btuart->proto.recv_buf(btuart, getTemp, size_end);
      }
}

void btuart_schedule_rx(struct tty_struct *tty, const __u8 *data, s32 count)
{
      btuart_t    *btuart = tty->disc_data;
      s32         free;

      /* Check if there is data that hasn't been passed up yet. 
       * In that case there is a task scheduled for this and we shouldn't 
       * add another one. */

      if (btuart->hci_data.put == btuart->hci_data.get) {
            tasklet_hi_schedule(&btuart->rx_task);
      }

      if (btuart->hci_data.put >= btuart->hci_data.get) {
            /* check for overruns... */
            if (btuart->hci_data.put + count - BT_INBUFFER_SIZE >= btuart->hci_data.get) {
                  printk("btuart.c : Buffer overrun!\n");
            } else {
                  /* Calculate how much space there is left at the end 
                   *                   of the buffer */
                  free = btuart->hci_data.tail - btuart->hci_data.put + 1;

                  /* normal case, data fits in buffer */
                  if (count < free) {
                        memcpy(btuart->hci_data.put, (u8*)data, count);
                  } else {
                        /* wrap buffer */
                        memcpy(btuart->hci_data.put, (u8*)data, free);
                        memcpy(btuart->hci_data.head, (u8*)(data + free), count - free);    
                  }
            }
      } else { 
            /* hci_data.put < hci_data.get */
            /* check for overruns ... */
            if (btuart->hci_data.put + count >= btuart->hci_data.get) {
                  printk("btuart.c : Buffer overrun!\n");
            } else {
                  /* Copy the data to the buffer */
                  memcpy(btuart->hci_data.put, (u8*)data, count);
            }
      }

      btuart->hci_data.put += count;
      if (btuart->hci_data.put > btuart->hci_data.tail)
            btuart->hci_data.put -= BT_INBUFFER_SIZE; 
}
#endif

/*
 * Function btuartld_receive_room (tty)
 *
 *    Used by the TTY to find out how much data we can receive at a time
 * 
*/
int  btuartld_receive_room(struct tty_struct *tty)
{
      DBFENTER;
      return 65536;  /* We can handle an infinite amount of data. :-) */
}

void btuartld_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
      btuart_t    *btuart = tty->disc_data;
      //char            data[TTY_FLIPBUF_SIZE];
      //unsigned long   flags;

      if (!btuart || !test_bit(BTUART_RUNNING, &btuart->flags) || !hcidev_present(btuart->hci))
            return;
      //local_irq_save(flags);
      read_lock(&btuart->lock);
#ifdef CONFIG_AFFIX_UART_NEW_BH
      /* store in bt inbuffer and schedule task if none is started  */
      btuart_schedule_rx(tty, cp, count);
#else
      {
            int   i;
            for (i = 0; i < count; i++) {
                  if (fp[i])
                        BTDEBUG("FLAG ERROR: %#02x\n", fp[i]);
            }
      }
      if (btuart->proto.recv_buf)
            btuart->proto.recv_buf(btuart, cp, count);
#endif
      read_unlock(&btuart->lock);
}

/*
 * Function btuartld_write_wakeup (tty)
 *
 *    Called by the driver when there's room for more data.  If we have
 *    more packets to write, we write them here.
 *
 */
void btuartld_write_wakeup(struct tty_struct *tty)
{
      btuart_t    *btuart = (btuart_t*) tty->disc_data;

      DBFENTER;
      if (!btuart || !test_bit(BTUART_RUNNING, &btuart->flags) || !hcidev_present(btuart->hci))
            return;
      read_lock(&btuart->lock);
      btuart_xmit_wakeup(btuart);
      read_unlock(&btuart->lock);
      DBFEXIT;
}

/*
 * btuart_init register line discipline for serial tty
 */
int __init init_btuart(void)
{
      int   err;

      DBFENTER;

      btl_head_init(&btuarts);
      /* Fill in our line protocol discipline, and register it */
      memset(&btuart_ldisc, 0, sizeof(btuart_ldisc));

      btuart_ldisc.magic = TTY_LDISC_MAGIC;
      btuart_ldisc.name  = "n_affix";
      btuart_ldisc.flags = 0;
      btuart_ldisc.open  = btuartld_open;
      btuart_ldisc.close = btuartld_close;
      btuart_ldisc.read  = NULL;
      btuart_ldisc.write = NULL;
      btuart_ldisc.ioctl = (void*)btuartld_ioctl;
      btuart_ldisc.poll  = NULL;
      btuart_ldisc.receive_buf  = btuartld_receive_buf;
      btuart_ldisc.receive_room = btuartld_receive_room;
      btuart_ldisc.write_wakeup = btuartld_write_wakeup;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)
      btuart_ldisc.owner = THIS_MODULE;
#endif
      err = tty_register_ldisc(N_BTUART, &btuart_ldisc);
      if (err) {
            BTERROR("Can't register line discipline (err = %d)\n", err);
            goto exit;
      }
      
      err = affix_set_uart(&btuart_ops);

      printk("Affix UART Bluetooth driver loaded (affix_uart)\n");
      printk("Copyright (C) 2001, 2002 Nokia Corporation\n");
      printk("Written by Dmitry Kasatkin <dmitry.kasatkin@nokia.com>\n");
      
exit: 
      DBFEXIT;
      return err;
}

void __exit exit_btuart(void)
{
      int err;
  
      DBFENTER;

      affix_set_uart(NULL);

      if ((err = tty_register_ldisc(N_BTUART, NULL))) {
            BTERROR("can't unregister line discipline (err = %d)\n", err);
      }

      DBFEXIT;
}


/*  If we are resident in kernel we want to call init_btuart_cs manually. */
module_init(init_btuart);
module_exit(exit_btuart);

MODULE_AUTHOR("Dmitry Kasatkin <dmitry.kasatkin@nokia.com>");
MODULE_DESCRIPTION("Affix UART driver");
MODULE_LICENSE("GPL");

EXPORT_SYMBOL(affix_uart_attach);
EXPORT_SYMBOL(affix_uart_detach);
EXPORT_SYMBOL(affix_uart_suspend);
EXPORT_SYMBOL(affix_uart_resume);


Generated by  Doxygen 1.6.0   Back to index