/* 
 * bluetooth.c -- Linux kernel integration code for bluetooth stack
 *
 * Copyright (C) 2000  Axis Communications AB
 *
 * Mattias Agren <mattias.agren@axis.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: bluetooth.c,v 1.72 2000/10/27 15:07:26 mattias Exp $
 */
 
#include <linux/bluetooth/sysdep-2.1.h>
#include <linux/config.h>
#include <linux/termios.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/delay.h>

#include <linux/bluetooth/bluetooth.h>
#include <linux/bluetooth/btcommon.h>
#include <linux/bluetooth/btmem.h>
#include <linux/bluetooth/hci.h>
#include <linux/bluetooth/l2cap.h>
#include <linux/bluetooth/rfcomm.h>
#include <linux/bluetooth/sdp.h>
#include <linux/bluetooth/tcs.h>

#ifdef BT_USE_PROC
#include <linux/bluetooth/bt_proc.h>
#endif

/****************** DEBUG CONSTANT AND MACRO SECTION ************************/

#if BT_DRIVER_DEBUG
#define BT_DRIVER(fmt, args...) printk(BT_DBG_STR" (driver) " fmt, ## args)
#else
#define BT_DRIVER(fmt, args...)
#endif /* BT_DRIVER_DEBUG */

#if BT_DATAFLOW_DEBUG
#define PRINTPKT(str, data, len) print_data(str, data, len)
#else
#define PRINTPKT(str, data, len)
#endif

#if BT_DATA_DEBUG
#define BT_DATA(fmt, args...) printk(BT_DBG_STR fmt, ## args)
#else
#define BT_DATA(fmt, args...)
#endif /* BT_DATA_DEBUG */

#if BT_LDISC_DEBUG
#define BT_LDISC(fmt, args...) printk(BT_DBG_STR" (ldisc) " fmt, ## args)
#else
#define BT_LDISC(fmt, args...)
#endif /* BT_LDISC_DEBUG */

#ifndef PRINTPKT
#define PRINTPKT(str, data, len)
#endif

/****************** CONSTANT AND MACRO SECTION ******************************/

#define BT_PORT_0 /* Should be set from Config.in */

#ifdef BT_PORT_0
#define BT_PORT 0x3F8 
#define BT_IRQ 4
#else
/* ??? */
#endif

#define BLUETOOTH_TYPE_DATA 1
#define BLUETOOTH_TYPE_DATA_VOICE 2

#define BT_TTY_DRIVER_MAGIC 0xb100b100 /* check if free...*/
#define BT_TTY_MAJOR 124 /* Experimental use */

#define BT_PARANOIA_CHECK 1

/****************** TYPE DEFINITION SECTION *********************************/

/****************** LOCAL FUNCTION DECLARATION SECTION **********************/

static void bt_init_stack(void);
static s32 bt_ctrl_init(void);
static s32 bt_create_connection(bt_connection *bt, 
				struct tty_struct *upper_tty);

#ifdef __USE_OLD_SYMTAB__
/*
 * LINUX 2.0.X SPECIFIC CODE
 */
static s32 bt_tty_read (struct tty_struct *, struct file *, __u8 *, u32);

/* not needed since we write directly to ttyS0->driver->write (rs_write) */
static s32 bt_tty_write (struct tty_struct *, struct file *, const __u8 *, u32);

static s32 bt_tty_select (struct tty_struct *tty, struct inode *inode,
			  struct file *filp, s32 sel_type, select_table *wait);

#else
/*
 * LINUX 2.2.X SPECIFIC CODE
 */
static ssize_t
bt_tty_read(struct tty_struct *tty, struct file *file, u8 *buf, size_t nr);

static ssize_t
bt_tty_write(struct tty_struct *tty, struct file *file, const u8 *buf,
	     size_t nr);

static u32 bt_tty_poll(struct tty_struct *tty, struct file *file,
		       struct poll_table_struct *wait);
#endif

static s32 bt_tty_ioctl (struct tty_struct *, struct file *,u32, unsigned long);
static s32 bt_tty_open (struct tty_struct *);
static void bt_tty_close (struct tty_struct *);
static s32 bt_tty_room (struct tty_struct *tty);

/* glue */
static s32 bt_write_top(struct tty_struct * tty, s32 from_user,
			const u8 *buf, s32 count);

static void bt_receive_lower_stack (struct tty_struct *tty, const __u8 * cp,
				    char *fp, s32 count);
static void bt_tty_wakeup (struct tty_struct *tty);

static void bt_reset_session(s32 line);

/****************** GLOBAL VARIABLE DECLARATION SECTION *********************/

/****************** LOCAL VARIABLE DECLARATION SECTION **********************/

static struct bt_ctrl_struct bt_ctrl;

/* some defines for easier reading */
#define BTCTRL bt_ctrl /* the name of the struct that holds all data */
#define GET_UPPERTTY(line) (BTCTRL.session[line].upper_tty)
#define SESSIONSTATE(line) (BTCTRL.session[line].state)
#define NBR_ACTIVE BTCTRL.nbr_active
#define NBR_UPPER BTCTRL.nbr_upper

struct bt_stat_struct bt_stat;

static struct tty_driver bt_driver;

static s32 bluetooth_refcount;

static struct tty_struct *sertty;

static s32 bt_stack_initiated = 0;

/* used when copying data from user level */
static u8 *tmp_bt_buf = NULL;

static struct tty_struct *bt_table[BT_NR_PORTS];
static struct termios *bt_termios[BT_NR_PORTS]; 
static struct termios *bt_termios_locked[BT_NR_PORTS];

static struct wait_queue *connect_wq = NULL;
static s32 con_status;


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


/*

Short description of new stack integration in kernel
----------------------------------------------------


1) PPP over serial (standard way): 

  user app (pppd)
       / \
-------| |------------------
       \ /
  -------------
  |   tty      |
  |           <----> line discipline (PPP)
  -------------
  | tty driver | (serial driver)
  -------------
       / \
-------| |------------------
       \ /
       UART



2) PPP over bluetooth:

  user app (pppd)
       / \
-------| |------------------
       \ / ttyBT0
   --------------
   | tty | ldisc-> PPP ldisc
-----------------------------------------------
   | tty driver | (bluetooth driver)
   -------------
   |            | <-------        
   |  BT STACK  | <-----  |            ADDED
   |            | <---  | | 
   -------------      | | |
   |   ttyS0    |     V V V
   |       ldisc->  BT ldisc 
-----------------------------------------------
   | tty driver | (serial driver)
   -------------
        / \
--------| |------------------
        \ /
       UART

ttySx is a standard serial port tty.
ttyBTx has a separate major nbr and has been registered to 
use bt driver as tty driver.

ttySx is opened initially from a bluetooth control-application
which sets the bt line discipline and keeps the tty open.

When ttyBT is opened from pppd it is set to use ppp discipline 
which in turn uses the driver registered in its tty i.e the bt 
driver.

At the stack top it acts like a serial driver for ppp
At the bottom the stack acts like any line discipline used from 
the standard serial tty.


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


             - Glue Layers for stack - 

                "APPLICATION" (here:ttyBT)
       ---------------------------------------------
 TOP   | bt_recieve_top()  |    bt_write_top()     |
       ---------------------------------------------
                ^                       |
                |                       V
  
 
                     T H E  S T A C K 
     
                                     
                ^                       |
                |                       V
        ----------------------------------------------------
 BOTTOM | bt_receive_lower_stack()| bt_write_lower_driver()|
        ----------------------------------------------------
              "PHYSICAL DRIVER" (here:serial driver)


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

*/


/***********************************/
/* STACK TOP (TTY DRIVER FUNCTIONS)*/
/***********************************/

static s32
bt_open(struct tty_struct *tty, struct file * filp)
{
	s32 line;
	int ret_val;
	
	line = GET_TTYLINE(tty);

	BT_DRIVER("bt_open on line %d\n", line);

	ret_val = bt_register_tty(tty) ;

	if (ret_val<0)
		return ret_val;
	
	MOD_INC_USE_COUNT;

	/* store calling tty in control block */
	return ret_val;
}

static void
bt_close(struct tty_struct *tty, struct file * filp)
{
	s32 line;

	line = GET_TTYLINE(tty);

	BT_DRIVER("bt_close on line %d \n", line);

	/* FIXME - flush channels etc */
	/* close rfcomm channel associated with this tty ?? */

	if (bt_unregister_tty(tty, line) < 0)
		return ;

	if (NBR_UPPER == 0) {
		DSYS("No more upper tty:s lets shutdown stack...\n");
		bt_shutdown();
	}
	
	MOD_DEC_USE_COUNT;
}

 

static void
bt_put_char(struct tty_struct *tty, u8 ch)
{
	u8 c;
	c = ch;
	BT_DRIVER("bt_put_char %c\n", c);
	bt_write_top(tty, 0, &c, 1);
}

static void
bt_flush_chars(struct tty_struct *tty)
{
	/* FIXME */
	BT_DRIVER("bt_flush_chars (nothing done)\n");
}


static s32
bt_write_room(struct tty_struct *tty)
{
	int n;
	n=buf_write_room();;
	BT_DRIVER("bt_write_room : %d\n", n);
	return n;
}

static s32
bt_chars_in_buffer(struct tty_struct *tty)
{
	u16 n;
	n=buf_byte_count();
	BT_DRIVER("bt_chars_in_buffer : %d\n", n);  
	return n;
}


/* Must flush the buffer, otherwise chars_in_buffer returns nonzero
   and pppd hangs on the exit while doing the set line disc ioctl */
static void
bt_flush_buffer(struct tty_struct *tty)
{
	bt_tx_buf* tx_buf;
	BT_DRIVER("bt_flush_buffer\n");
	
	while((tx_buf = get_bt_buf())){
		unsubscribe_bt_buf(tx_buf);
	}
}

static s32
bt_ioctl(struct tty_struct *tty, struct file * file,
                    u32 cmd, unsigned long arg)
{
#define FNC "bt_ioctl: "
	s32 tmp;
	u32 utmp;
	s32 err = 0;
	s32 size = _IOC_SIZE(cmd);
	inquiry_results *inq_res;
	bt_connection btcon;
	u8 bd_addr[6];

#if 0
	/* We don't have to check this here since all commands not intended
	   for us will be forwarded to the serial driver */
	if (_IOC_TYPE(cmd) != BT_IOC_MAGIC) {
		return -EINVAL;
	}
	if (_IOC_NR(cmd) > BT_IOC_MAXNR) {
		return -EINVAL;
	}
#endif
 
	/* The direction is a bitmask, and VERIFY_WRITE catches R/W transfer.
	   'Type' is user-oriented, while verify_area is kernel-oriented, so
	   the concept of "read" and "write" is reversed */
	if (_IOC_DIR(cmd) & _IOC_READ) {
		err = verify_area(VERIFY_READ, (void*)arg, size);
		if (err)
			BT_DRIVER("verify_area(VERIFY_READ) error:%d\n",err);
	} else if (_IOC_DIR(cmd) & _IOC_WRITE) {
		err = verify_area(VERIFY_WRITE, (void*)arg, size);
		if (err)
			BT_DRIVER("verify_area(VERIFY_WRITE) error:%d\n",err);
	}
	if (err) {
		BT_DRIVER(FNC"BTCONNECT: error:%d\n",err);
		return err;  
	}

	switch (cmd) {
 
	case BTCONNECT:
	{
		BT_DRIVER(FNC"BT connect request\n");
		/* argument is an object with all info to start a remote 
		   connection */

		copy_from_user(&btcon, (s32*)arg, size);
        
		/* Init connection */
		return bt_create_connection(&btcon, tty);
	}
	break;  
	case BTDISCONNECT:
	{
		s32 line;
		BT_DRIVER(FNC"BTDISCONNECT not fully implemented, now only ");
		BT_DRIVER("disconnects RFCOMM for this tty\n");
		copy_from_user(&line, (s32*)arg, size); 
		rfcomm_disconnect_req(line);
	}
		break;
	case BTINQUIRY:
	{
		s32 i, max_nbr_of_units;
		u8 lap[3];
		
		BT_DRIVER(FNC"BTINQUIRY\n");
		
		copy_from_user(&max_nbr_of_units, (s32*)arg, 4);
		inq_res = hci_inquiry(lap, 10 , max_nbr_of_units);
		for (i = 0; i < inq_res->nbr_of_units; i++) {
			printk("BD %d: %02x:%02x:%02x:%02x:%02x:%02x\n",i,
			       inq_res->bd_addr[0+6*i],inq_res->bd_addr[1+6*i],
			       inq_res->bd_addr[2+6*i],inq_res->bd_addr[3+6*i],
			       inq_res->bd_addr[4+6*i],inq_res->bd_addr[5+6*i]);
		}
		copy_to_user((s32*)arg, inq_res, size + 6 * max_nbr_of_units);
		break;
	}
	case BTSETSPEED:
		BT_DRIVER(FNC"BTSETSPEED not implemented\n");
		break;
    
	case BTINITSTACK:
		bt_init_stack();
		break;

	case BTSHUTDOWN:
		bt_shutdown();
		break;
		
	case BTSETHARDWARE:
		DSYS("Setting HW\n");
		GET_USER(tmp, (s32*)arg);
		return bt_set_hw(tmp);
		break;

	case BTREADBDADDR:
		BT_DRIVER(FNC"BTREADBDADDR\n");
		read_bd_addr(bd_addr);
		{
			BD_ADDR rev_bd;
			u16 i;
			/* return as big endian */
			for (i = 0; i < 6; i++) {
				rev_bd[5-i] = bd_addr[i];
			}
			copy_to_user((s32*)arg, rev_bd, 6);
		}
		break;

	case BTWRITEERICSSONBDADDR:
		copy_from_user(&bd_addr, (s32*)arg, size);
		BT_DRIVER(FNC"setting bd on Ericsson HW to \n");
		PRINTPKT("bd :",(u8*)bd_addr,6);
		set_ericsson_bd_addr(bd_addr);
		break;  
     
	case BTWRITESCANENABLE:
		GET_USER(tmp, (s32*)arg);
		BT_DRIVER("setting write scan enable : [0x%x]\n", tmp);
		write_scan_enable(tmp);
		break;
		
	case BTWRITEPAGESCANACTIVITY:
	{
		u32 par[2];
		copy_from_user(&par, (s32*)arg, size);
		BT_DRIVER("setting write page scan activity : [0x%x,0x%x]\n", 
			  par[0], par[1]);
		write_pagescan_activity(par[0], par[1]);
	}
	break;
	
	case BTSETERICSSONBAUDRATE:
		
		/* Set baudrate in hardware */ 
		GET_USER(tmp, (s32*)arg);
		BT_DRIVER(FNC"Setting Ericsson module baudrate to %d\n",tmp);

		set_ericsson_baudrate(tmp);
		break;
    
	case BTSETDIGIBAUDRATE:
		/* Set baudrate in digi hardware */

		GET_USER(tmp, (s32*)arg);
		BT_DRIVER(FNC"Setting baudrate in digidongle to %d\n", tmp);
		set_digi_baudrate(tmp);
		break;       

	case BTSENDTESTDATA:
	{
		copy_from_user(&tmp, (s32*)arg, 4);
		copy_from_user(&utmp, (s32*)arg + 1, 4);
		rfcomm_send_testdata(tmp,utmp);
	}
	break;
	
	case BTSETLOCALNAME:
	{
		/* FIXME: If we send the length of the string too, we don't
		   have to copy all the 248 byte... */
		u8 local_name[248];
		copy_from_user(local_name, (s32*)arg, 248);
		hci_change_local_name(local_name);
	}
	break;

	case BTTESTCONNECTREQ:
	{
		u8 bd[6];
		copy_from_user(bd, (s32*)arg, 6);
		return hci_test_connect_req(bd);
	}
	break;
	
	case BTERICSSONTESTCONTROL:
	{
		u8 ctrl_seq[11];
		u16 hci_hdl;
		copy_from_user(ctrl_seq, (s32*)arg, 11);
		hci_hdl = CHAR2INT12(ctrl_seq[1], ctrl_seq[0]);
		hci_ericsson_test_control(hci_hdl, ctrl_seq + 2);
	}
	break;
		
	case BTERICSSONENTERTESTMODE:
	{
		u16 hci_hdl;
		copy_from_user(&hci_hdl, (s32*)arg, 2);
		hci_ericsson_enter_test_mode(hci_hdl);
	}
	break;

	 
	case BTERICSSONENABLEDUT:
	{
		hci_enable_dut();
	}
	break;


	case BTERICSSONTXTEST:
	{
		u8 ctrl_seq[12];

		copy_from_user(ctrl_seq, (s32*) arg, 12);
		hci_ericsson_tx_test(ctrl_seq);
	}
	break;
	
	case BTSYNTHON:
	{
		synth_on();
	}
	break;
	
#if 0 
	/* deliver this to user mode later or use /proc */
	case BTPRINTSTACKSTATUS:
	{
		BT_DRIVER("Getting stack status\n");
		bt_print_stack_status();
	}
	break;
#endif
	default:
		/* forward rest to serial driver ! */
		BT_DRIVER(FNC"forwarding ioctl 0x%x to serial driver\n", cmd); 
   
		/* FIXME - this MUST be set before using bt driver !! */
		if (sertty != NULL)
			return sertty->driver.ioctl(sertty, file, cmd, arg);
		else
			D_ERR(FNC"no sertty set !!\n");	
	}
  
	return 0;
#undef FNC
}

static void
bt_throttle(struct tty_struct * tty)
{
	BT_DRIVER("bt_throttle (nothing done)\n");
}

static void
bt_unthrottle(struct tty_struct * tty)
{
	BT_DRIVER("bt_unthrottle (nothing done)\n");
}


static void
bt_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
	BT_DRIVER("bt_set_termios - forwarding to serial driver\n");

	/* modify serial tty not bt tty */
	if (sertty != NULL)
		sertty->driver.set_termios(sertty, old_termios);
	else
		D_ERR("bt_set_termios : no sertty set !!\n");
}

static void bt_start(struct tty_struct *tty)
{
	BT_DRIVER("bt_start (nothing done)\n");
}

static void
bt_stop(struct tty_struct *tty)
{
	BT_DRIVER("bt_stop (nothing done)\n");
}

void
bt_hangup(struct tty_struct *tty)
{
	BT_DRIVER("bt_hangup (nothing done)\n");
}

/********************************************/
/* STACK BOTTOM FUNCTIONS (LINE DISCIPLINE )*/
/********************************************/

/*
 * TTY callbacks
 */

#ifdef __USE_OLD_SYMTAB__
/*****************************************************************************/
/* LINUX 2.0.X SPECIFIC CODE                                                 */
/*****************************************************************************/
static s32 bt_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
			u32 nr)
#else
/*****************************************************************************/
/* LINUX 2.2.X SPECIFIC CODE                                                 */
/*****************************************************************************/
static ssize_t bt_tty_read(struct tty_struct * tty, struct file * file,
			   u8 * buf, size_t nr)
#endif
{
	/* should not read data directly over serial tty */
	BT_LDISC("bt_tty_read (disabled)\n");
	return 0;
}

#ifdef __USE_OLD_SYMTAB__
/*****************************************************************************/
/* LINUX 2.0.X SPECIFIC CODE                                                 */
/*****************************************************************************/
static s32
bt_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
	      u32 count)
#else
/*****************************************************************************/
/* LINUX 2.2.X SPECIFIC CODE                                                 */
/*****************************************************************************/
static ssize_t
bt_tty_write(struct tty_struct * tty, struct file * file, const u8 * data,
	     size_t count)
#endif
{
	/* should simply discard data since ttySx only exists for internal use
	 */
	BT_LDISC("bt_tty_write (%d) done !x\n", count);
  
	return tty->driver.write(tty,1/*from user*/, data, count );
//	PRINTPKT("ttySx wrote : ", (u8*)data, (s32)count);
//	return count;
}


static s32
bt_tty_ioctl (struct tty_struct *tty, struct file * file,
	      u32 cmd, unsigned long arg)
{
#define FNC "bt_tty_ioctl: "
	BT_LDISC("bt_tty_ioctl cmd 0x%x\n", cmd);
  
	/* FIXME - paranoia check */
  
	switch (cmd) {

	default:
		/* forward rest to n_tty line discipline, it takes care of
		   termios settings etc, the rest goes to the tty driver 
		   (serial driver) */
		BT_LDISC(FNC"forwarding ioctl 0x%x to n_tty line disc\n", cmd);
		return n_tty_ioctl(tty, file, cmd, arg);
	}
	return 0;
#undef FNC
}

#ifdef __USE_OLD_SYMTAB__
/*****************************************************************************/
/* LINUX 2.0.X SPECIFIC CODE                                                 */
/*****************************************************************************/
static s32
bt_tty_select (struct tty_struct *tty, struct inode *inode,
	       struct file *filp, s32 sel_type, select_table * wait)
{
	BT_LDISC("bt_tty_select (nothing done!)\n");
	return 0;
}
#else
/*****************************************************************************/
/* LINUX 2.2.X SPECIFIC CODE                                                 */
/*****************************************************************************/
static u32 bt_tty_poll(struct tty_struct *tty,
                                struct file *file,
                                struct poll_table_struct *wait)
{
	BT_LDISC("bt_tty_poll (nothing done!)\n");
	return 0;
}
#endif


static s32
bt_tty_open (struct tty_struct *tty)
{
	BT_LDISC("bt_tty_open\n");
	DSYS("Setting BT driver to use serial tty\n");
	sertty = tty;
	return 0;
}


static void
bt_tty_close (struct tty_struct *tty)
{
	struct tty_struct *upper_tty;
	s32 line = 0;
	
	/* Now hangup all active upper tty:s */
	
	while (line < BT_NR_PORTS)
	{
		if ((SESSIONSTATE(line) == BT_ACTIVE) ||
		    (SESSIONSTATE(line) == BT_UPPERCONNECTED))
		{
			BT_LDISC("Hanging up line %d\n", line);
			upper_tty = GET_UPPERTTY(line);
			if (upper_tty)
				tty_hangup(upper_tty);
		}
		line++;
	}
}

/*
 * Callback function from tty driver. Return the amount of space left
 * in the receiver's buffer to decide if remote transmitter is to be
 * throttled.
 */

static s32
bt_tty_room (struct tty_struct *tty)
{
	BT_LDISC("bt_tty_room (return 65536 !)\n");
	return 65536;	    /* We can handle an infinite amount of data. :-) */
}

/*
 * This function is called by the tty driver when the transmit buffer has
 * additional space. 
 */

static void
bt_tty_wakeup (struct tty_struct *tty)
{
	printk("bt_tty_wakeup : not done\n");
}


/************************/
/* glue layer functions */
/************************/

/*
 * HCI calls this function to write to lower layer 
 * driver
 */

s32
bt_write_lower_driver(u8 *data, s32 len)
{
#define FNC "bt_write_lower_driver: "

	if (len < 0) /* (!) */
	{
		D_ERR("Can't write neg length...\n");
		return 0;
	}
	
	/* get lower layer tty and call its write function directly 
	   without using its registered line discipline */

	if (sertty) {
		s32 i = 0;
		s32 tmp;
		s32 sent;
		
		if ((tmp=sertty->driver.write_room(sertty)) < len){
			printk("ser tx buffers can't send all, wait (%d/%d)\n",
			       tmp, len);
			return 0;
		} else {
			sent = sertty->driver.write(sertty, 0, data, len);
			PRINTPKT("bt_write_lower_driver :", (u8*)data, len);
		}
		
		if (sent != len) {
			printk(FNC"Only sent %d of total %d\n", 
			       sent, len);
		}
		len -= sent;

		/* should this be used ? */
#if 0
		while (len) {
		  
		  /* FIXME -- udelay is _bad_ */
			if (i > 100) {
			  D_ERR(FNC"Sent only %d bytes of the remaining %d\n",
				tmp, len);
			  break;
			}
			
			udelay(1000);
			udelay(1000);
			printk("send rest...\n");
			tmp = sertty->driver.write(sertty, 0, data + sent,len);
			len -= tmp;
			sent += tmp;
			i++;
		}
#endif
		return sent;
	} else {
		D_ERR(FNC"no sertty is set\n");
	}
	return 0;
#undef FNC
}

/*
 * Callback function when data is available at the tty driver.
 */

static void
bt_receive_lower_stack (struct tty_struct *tty, const __u8 *data,
			char *flags, s32 count)
{
	BT_DATA("bt_receive_lower_stack %d bytes\n", count);
  	PRINTPKT("bt_receive_lower_stack :", (u8*)data, count);

	/* Sends the received data to HCI */
 
	/* TEST - don't accept more until this is finished... */
	//tty->driver.throttle(tty);
	hci_receive_data((u8*)data, count);
	/* ok, accept more again... */
	//tty->driver.unthrottle(tty);
}

/* 
 * Upper tty writes to top of stack (BT drivers write()) 
 */
static s32
bt_write_top(struct tty_struct * tty, s32 from_user,
			const u8 *buf, s32 count)
{
#define FNC "bt_write_top"
	s32 line;
	s32 bytes_sent;
	struct bt_session *bt;

	line = GET_TTYLINE(tty);

	BT_DATA(FNC": %d bytes on line %d\n", 
		count, line);
	
	bt = (bt_session *)tty->driver_data;
	
	if ((bt->rfcomm != NULL) && (bt->rfcomm->magic != RFCOMM_MAGIC)) {
		D_ERR(FNC": rfcomm magic failed (0x%x != 0x%x)\n", 
		      bt->rfcomm->magic, RFCOMM_MAGIC);
		return -1;
	}

	if (SESSIONSTATE(line) != BT_ACTIVE)
	{
		BT_DATA("bt_write_top : line %d is not in active state\n",
			line);
		return 0;
	}

	if (from_user) {
		copy_from_user(tmp_bt_buf, buf, count);
		bytes_sent = rfcomm_send_data((u8*)tmp_bt_buf, count,
					      line, bt->dlci);
	} else {
		bytes_sent = rfcomm_send_data((u8*)buf, count, 
					      line, bt->dlci);
	}

	BT_DATA(FNC": wrote %d bytes on line %d (dlci %d)\n", 
		bytes_sent, line, bt->dlci);

	bt_stat.bytes_sent+=bytes_sent;

	return bytes_sent;
#undef FNC
}

/*
 * RFCOMM calls this function to write to higher layer 
 * "application" e.g ttyBT
 */

s32
bt_receive_top(rfcomm_con *rfcomm, u8 *data, s32 len) 
{
	struct tty_struct *upper_tty;

	/* get upper tty and call its ldisc->receive_buf */

	BT_DATA("bt_receive_top : %d bytes on line %d\n", len, rfcomm->line);

	if (SESSIONSTATE(rfcomm->line) != BT_ACTIVE) {
		/* change debug macro... */
		BT_DATA("bt_receive_top : (%d) line %d not active, silent discard...\n", 
		     len, rfcomm->line, SESSIONSTATE(rfcomm->line));
		return 0;
	}

	bt_stat.bytes_received+=len;

	upper_tty = GET_UPPERTTY(rfcomm->line);

	if (upper_tty) {
		upper_tty->ldisc.receive_buf(upper_tty, data, NULL, len);  
	} else {
		D_ERR("No upper tty registered !!!\n");
		return -1;
	}
	return 0;
}


void bt_feedstack(void)
{
#define FNC "bt_feedstack: "
	s32 nbr_checked = 0;
	s32 line = bt_ctrl.tty_last_unthrottled;
	struct tty_struct *upper_tty = NULL;

	if (NBR_ACTIVE == 0)
		return;

	/* if buffer usage is less than BTMEM_UNTHROTTLE_SIZE wake up ldisc */
	if (buf_byte_count() < BTMEM_UNTHROTTLE_SIZE) {

		if (line == BT_NR_DATAPORTS)
			line = 0; /* last port, now start over */
		
		/* Choose an active upper tty */
		
		/* FIXME -- How do we choose this best ? 
		   For now simply take each active tty in turn */	
		
		while (nbr_checked < NBR_ACTIVE) {

			BT_DATA("bt_feedstack : check line %d\n", line);
			
			/* skip non-active and control port (last) */
			while ((SESSIONSTATE(line) != BT_ACTIVE) && 
			       (line < BT_NR_DATAPORTS)) 
				line++;
			
			if (line < BT_NR_DATAPORTS) {	
				/* found an active */
				BT_DATA("bt_feedstack : wakeup line %d !\n", 
					line);
				upper_tty = GET_UPPERTTY(line);
				bt_ctrl.tty_last_unthrottled = line;
				break;
			} else {
				DSYS("No active line to feed from!\n");
				return;
			}

			nbr_checked++;
		}

		if (!upper_tty)
			return;

		if ((upper_tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
		    && upper_tty->ldisc.write_wakeup) {
			BT_DATA(FNC"write_wakeup ldisc!\n");
			/* TTY_DO_WRITE_WAKEUP bit is cleared in 
			   upper_tty->flags which means user mode process 
			   will be able to write more data */

			(upper_tty->ldisc.write_wakeup)(upper_tty);
		}
		else if (!upper_tty->ldisc.write_wakeup) {     
			/* if no wake_up function is defined (N_TTY ldisc)
			   wake up wait queue */
			BT_DATA(FNC"write_wakeup ldisc (N_TTY)\n");
			wake_up_interruptible(&upper_tty->write_wait);
		}
	}
#undef FNC
}


/*
 * FIXME !!!
 */

s32
bt_create_connection(bt_connection *bt, struct tty_struct *upper_tty)
{
#define FNC "bt_create_connection: "
	s32 line;

	PRINTPKT("bt_connection obj", (u8*)bt, 16);

	line = GET_TTYLINE(upper_tty);
	
	switch (bt->profile) {
		
	case SERPORT_PROFILE:
	{
		/* 
		 *  No client functionality yet with new SDP
		 *  only server
		 */
		
		rfcomm_connect_req(bt->bd, bt->line, bt->server_chn);
		BT_DRIVER(FNC"No client stuff yet...\n");
		break;
		
	case LAN_PROFILE:
		
		/* 
		 *  No client functionality yet with new SDP
		 *  only server
		 */
		
		rfcomm_connect_req(bt->bd, bt->line, bt->server_chn);
		BT_DRIVER(FNC"No client stuff yet...\n");
		break;  
		
	default:    
		BT_DRIVER(FNC"Profile:%d not supported\n",bt->profile);
		break;
	}
	}
	
	return 0;
#undef FNC
}


/* notified upper tty application that this line is down */
void
bt_hangupline(s32 line)
{
  struct tty_struct *upper_tty;

  BT_DRIVER("bt_hangupline : hanging up line %d\n", line);

  /* find corresponding upper tty */
  upper_tty = GET_UPPERTTY(line);
  
  if (upper_tty)
	  tty_hangup(upper_tty);
  else
	  D_ERR("bt_hangupline : no upper tty\n");
}



/**********************************/
/* Bluetooth Stack Initialization */
/**********************************/

s32
bt_init(void)
{
	static struct tty_ldisc	bt_ldisc;
	s32 status;
	s32 procfs_status = -1;
	

	show_bt_version();

	/* Initialize the tty_driver structure */
	
	memset(&bt_driver, 0, sizeof(bt_driver));
	bt_driver.magic = BT_TTY_DRIVER_MAGIC;
	bt_driver.name = "ttyBT";
	bt_driver.major = BT_TTY_MAJOR;
	bt_driver.minor_start = 0;
	bt_driver.num = BT_NR_PORTS;
	bt_driver.type = TTY_DRIVER_TYPE_SERIAL;
	bt_driver.subtype = BLUETOOTH_TYPE_DATA;
	bt_driver.init_termios = tty_std_termios;
	bt_driver.init_termios.c_cflag =
		B115200 | CS8 | CREAD | HUPCL | CLOCAL;
 
	/* no output processing */
	bt_driver.init_termios.c_oflag = 0;
	/* no echo ... */
	bt_driver.init_termios.c_lflag = 0;
	
	bt_driver.flags = TTY_DRIVER_REAL_RAW;
	bt_driver.refcount = &bluetooth_refcount;
 	bt_driver.table = bt_table; 

	/* temp setting, should point to serial drivers termios */
	bt_driver.termios = bt_termios;
	bt_driver.termios_locked = bt_termios_locked;
 
	bt_driver.open = bt_open;
	bt_driver.close = bt_close;
	bt_driver.write = bt_write_top;
	bt_driver.put_char = bt_put_char;
	bt_driver.flush_chars = bt_flush_chars;
	bt_driver.write_room = bt_write_room;
	bt_driver.chars_in_buffer = bt_chars_in_buffer;
	bt_driver.flush_buffer = bt_flush_buffer;
	bt_driver.ioctl = bt_ioctl;
	bt_driver.throttle = bt_throttle;
	bt_driver.unthrottle = bt_unthrottle;
	bt_driver.set_termios = bt_set_termios;
	bt_driver.stop = bt_stop;
	bt_driver.start = bt_start;
	bt_driver.hangup = bt_hangup;
  
	if (tty_register_driver(&bt_driver))
		panic("Couldn't register bluetooth driver\n");

	DSYS("Bluetooth driver registered in %s\n", bt_driver.name);

/*
 * Register the tty discipline
 */
	
	(void) memset (&bt_ldisc, 0, sizeof (bt_ldisc));
	bt_ldisc.magic		= TTY_LDISC_MAGIC;
	bt_ldisc.open		= bt_tty_open;
	bt_ldisc.close		= bt_tty_close;
	bt_ldisc.read		= bt_tty_read;
	bt_ldisc.write		= bt_tty_write; /* bypass stack ...*/
	bt_ldisc.ioctl		= bt_tty_ioctl;
#ifdef __USE_OLD_SYMTAB__
	bt_ldisc.select	        = bt_tty_select;
#else
	bt_ldisc.poll  	        = bt_tty_poll;
#endif
	bt_ldisc.receive_room	= bt_tty_room;
	bt_ldisc.receive_buf	= bt_receive_lower_stack;
	bt_ldisc.write_wakeup	= bt_tty_wakeup;
	
	status = tty_register_ldisc (N_BT, &bt_ldisc);
	if (status == 0) {
		DSYS("Bluetooth line discipline registered.\n");
	} else {
		printk (KERN_ERR "error registering line discipline: %d\n",
			status);
		return status;
	}

	/* Initialize the tty_driver structure */
	bt_ctrl_init();

	sertty = NULL;

#ifdef BT_USE_PROC

	DSYS("Registering BT proc files\n");

#ifdef KERNEL_VERSION /* Not defined for kernel 2.0.x */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
	procfs_status = proc_register(&proc_root, &bt_status);
#else
	procfs_status = proc_register_dynamic(&proc_root, &bt_status);
#endif /* LINUX_VERSION_CODE */
#else  /* KERNEL_VERSION */

	procfs_status = proc_register_dynamic(&proc_root, &bt_status);
#endif/* KERNEL_VERSION */

	if (procfs_status < 0) {
		D_ERR("Couldn't register proc file bt_status %d\n", 
		      procfs_status);
	}
	
#ifdef KERNEL_VERSION /* Not defined for kernel 2.0.x */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
	procfs_status = proc_register(&proc_root, &bt_internal_info);
#else
	procfs_status = proc_register_dynamic(&proc_root, &bt_internal_info);
#endif /* LINUX_VERSION_CODE */
#else  /* KERNEL_VERSION */
	procfs_status = proc_register_dynamic(&proc_root, &bt_internal_info);
#endif/* KERNEL_VERSION */

	if (procfs_status < 0) {
		D_ERR("Couldn't register proc file bt_internal_info %d\n",
		      procfs_status);
	}

#ifdef KERNEL_VERSION /* Not defined for kernel 2.0.x */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
	procfs_status = proc_register(&proc_root, &sdp_proc_entry);
#else
	procfs_status = proc_register_dynamic(&proc_root, &sdp_proc_entry);
#endif /* LINUX_VERSION_CODE */
#else  /* KERNEL_VERSION */
	procfs_status = proc_register_dynamic(&proc_root, &sdp_proc_entry);
#endif/* KERNEL_VERSION */

	if (procfs_status < 0) {
		D_ERR("Couldn't register proc file for sdp database %d\n",
		      procfs_status);
	}


#ifdef KERNEL_VERSION /* Not defined for kernel 2.0.x */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
	procfs_status = proc_register(&proc_root, &bt_proc_doit);
#else
	procfs_status = proc_register_dynamic(&proc_root, &bt_proc_doit);
#endif /* LINUX_VERSION_CODE */
#else  /* KERNEL_VERSION */

	procfs_status = proc_register_dynamic(&proc_root, &bt_proc_doit);
#endif/* KERNEL_VERSION */

	if (procfs_status < 0) {
		D_ERR("Couldn't register proc file bt_status %d\n", 
		      procfs_status);
	}
	

#endif /* BT_USE_PROC */

	DSYS("Bluetooth Driver Using ttyBT[0-%d] (data), ttyBTC (ctrl [%d])\n",
	     BT_NR_DATAPORTS-1, BT_NR_PORTS-1);

	return 0; /*success*/
}

s32
bt_initiated()
{
	return bt_stack_initiated;
}


void
bt_init_stack()
{
	unsigned long page;
	
	/* shutdown if already initiated */
	if (bt_stack_initiated)
		bt_shutdown();

	/* The following part of the code is originally from the Linux serial 
	   driver (serial.c). The last conditional is used to prevent that only
	   one "tmp_bt_buf" is allocated even if many tty's are opened. This
	   is because the kernel may schedule after get_free_page is called. */
	
	if (!tmp_bt_buf) {
		page = get_free_page(GFP_KERNEL);
		if (!page)
			return;
		
		if (tmp_bt_buf)
			free_page(page);
		else
			tmp_bt_buf = (u8 *) page;
	}

	/* Initialise all layers in the bluetooth stack */
	
	DSYS("Initialising Bluetooth Stack\n");
	hci_init();
	l2cap_init();
	rfcomm_init();
	sdp_init();
	tcs_init();
	btmem_init(); 
	bt_stack_initiated = 1;
	bt_stat.bytes_received=0;
	bt_stat.bytes_sent=0;
}

s32
bt_ctrl_init()
{
	s32 i;
	BT_DRIVER("Initiating bt ctrl struct\n");

	for (i = 0; i < BT_NR_PORTS; i++)
		bt_reset_session(i);

	bt_ctrl.nbr_upper = 0;
	bt_ctrl.nbr_active = 0;
	bt_ctrl.tty_last_unthrottled = 0 ; 

	return 1;
}
 
void bt_reset_session(s32 line)
{
	bt_ctrl.session[line].upper_tty = NULL;
	bt_ctrl.session[line].rfcomm = NULL;
	bt_ctrl.session[line].dlci = 0; 
	bt_ctrl.session[line].state = BT_INACTIVE;
}

void
show_bt_version(void)
{
	printk("Bluetooth Driver v1.2, Copyright (c) 2000 Axis Communications AB\n");
}

u8 *statename(s32 state)
{
	switch(state)
	{
	case BT_INACTIVE:
		return "BT_INACTIVE";
	case BT_LOWERCONNECTED:
		return "BT_LOWERCONNECTED";
	case BT_UPPERCONNECTED:
		return "BT_UPPERCONNECTED";
	case BT_ACTIVE:
		return "BT_ACTIVE";
	default:
		return "Unknown";	
	}
}


s32 bt_sprintf_status(u8 *buf)
{
	s32 pos = 0;
	s32 line;

	/* print all active sessions */
	pos += sprintf(buf+pos, "\n[BT Interface]\n");
	for (line = 0; line < BT_NR_PORTS;line++)
	{
		pos += sprintf(buf+pos, "line[%d] state : %s\n", line,
			       statename(SESSIONSTATE(line)));
	}
	return pos;
}


/*
 * FIXME -- make this register function more general i.e
 * enable registering any layer. 
 * Parameters : line, protocol type, protocol specific struct
 */
s32
bt_register_rfcomm(struct rfcomm_con *rfcomm, u8 dlci)
{
	s32 line = 0;
	s32 found = 0;

	if (!bt_stack_initiated)
		return -1;	

	line = rfcomm->line;

	if ((SESSIONSTATE(line) == BT_INACTIVE) || 
	    (SESSIONSTATE(line) == BT_UPPERCONNECTED)) 
	{
		/* now register ! */
		DSYS("bt_register_rfcomm : dlci %d on line %d\n", 
		     dlci, line);
		
		bt_ctrl.session[line].rfcomm = rfcomm; 
		bt_ctrl.session[line].dlci = dlci;
		if (SESSIONSTATE(line) == BT_INACTIVE)
			SESSIONSTATE(line) = BT_LOWERCONNECTED;
		else {	
			SESSIONSTATE(line) = BT_ACTIVE;
			NBR_ACTIVE++;
		}
		found = 1;
	}
	
	if (!found)
		D_WARN("bt_register_rfcomm : could not find any available lines\n");
	return found;
}

s32
bt_unregister_rfcomm(s32 line)
{
	BT_DRIVER("bt_unregister_rfcomm : line %d\n", line);
	if (SESSIONSTATE(line) == BT_ACTIVE) {
		bt_ctrl.session[line].rfcomm = 0; 
		bt_ctrl.session[line].dlci = 0;

		/* should rfcomm always send bt_hangupline 
		   when disconnecting ? */

                BT_DRIVER("Upper tty still open...\n");
                SESSIONSTATE(line) = BT_UPPERCONNECTED;
		NBR_ACTIVE--;
        }
        else if (SESSIONSTATE(line) == BT_LOWERCONNECTED) {
		bt_reset_session(line);
        }
	else {
		D_WARN("bt_unregister_rfcomm : inactive session\n");
		return -1;
	}
	return 0;
}

s32
bt_register_tty(struct tty_struct *tty)
{
	s32 line;

	line = GET_TTYLINE(tty);
  
	DSYS("Registering tty on line %d\n", line);

	if ((SESSIONSTATE(line) == BT_LOWERCONNECTED) ||
                (SESSIONSTATE(line) == BT_INACTIVE)) {
		
		NBR_UPPER++;
		bt_ctrl.session[line].upper_tty = tty;
		tty->driver_data = &bt_ctrl.session[line];
                if (SESSIONSTATE(line) == BT_INACTIVE)
                        SESSIONSTATE(line) = BT_UPPERCONNECTED;
                else {
                        SESSIONSTATE(line) = BT_ACTIVE;
			NBR_ACTIVE++;
		}
		return 0;
	} else {
		D_WARN("bt_register_tty : line busy !\n");
	}
	return -EBUSY;
}

s32
bt_unregister_tty(struct tty_struct *tty, s32 line)
{
	DSYS("Unregistering tty on line %d\n", line);
	
	/* Check that it is ok to close this line ... 
	   (flush buffers etc etc etc )*/
	
	if ((SESSIONSTATE(line) == BT_ACTIVE) || 
	    (SESSIONSTATE(line) == BT_UPPERCONNECTED)) {
	
                if (SESSIONSTATE(line) == BT_ACTIVE)
			SESSIONSTATE(line) = BT_LOWERCONNECTED;
                else
                        bt_reset_session(line);
		NBR_ACTIVE--;
		NBR_UPPER--;
	}
	else {
		D_WARN("bt_unregister_tty : tty not prev registered\n");
		return -ENODEV;
	}
	return 0; /*Until the above is fixed */
}

void
bt_shutdown(void)
{
	s32 i;
	struct bt_session *bt;
	DSYS("Shutting down bluetooth stack\n");

	if (!bt_stack_initiated) {
		D_WARN("Bluetooth stack not initiated\n");
		return;
	}

	/* disconnect all active connections */
	for (i = 0; i < BT_NR_DATAPORTS;i++) {
		
		bt = &(bt_ctrl.session[i]);
		if ((bt->state == BT_ACTIVE) || 
		    (bt->state == BT_LOWERCONNECTED)) {
			DSYS("Disconnecting dlci %d on line %d\n", 
			     bt->dlci, i);
			rfcomm_disconnect_req(i);
			bt->dlci = 0;
			bt->rfcomm = NULL;
		}
	}

	rfcomm_close();
	sdp_shutdown();
	tcs_shutdown();
	l2cap_shutdown();
	btmem_shutdown();
	bt_stack_initiated = 0;

	if (tmp_bt_buf) {
		free_page((unsigned long) tmp_bt_buf);
		tmp_bt_buf = NULL;
	}
}

/* 
 * HW initialization 
 */

s32 bt_set_hw(s32 hw_type)
{
	DSYS("Current HW set to %s\n", get_hw_name(hw_type));

	if (!bt_initiated())
		bt_current_hw=hw_type;
	else
		D_WARN("Cannot change HW when stack is active\n");

	switch (hw_type)
	{
	case HW_NOINIT:
		break;
		
	case HW_ERICSSON:
		break;
		
	case HW_DIGIANSWER_PC:
		break;
		
	case HW_DIGIANSWER_RS232:
		break;
		
	case HW_CSR:
		break;
		
	case HW_XIRCOMM:
		break;      

	case HW_USBMODULE:
		break;      
		
	default:
		break;  
	}
	return 0;
}


char *get_hw_name(s32 hw_type)
{
	switch (hw_type)
	{
	case HW_NOINIT:
		return "HW no init";
		break;

	case HW_ERICSSON:
		return "Ericsson";
		break;
		
	case HW_DIGIANSWER_PC:
		return "DigiAnswer PC card";
		break;
		
	case HW_DIGIANSWER_RS232:
		return "DigiAnswer RS232 dongle";
		break;
		
	case HW_CSR:
		return "CSR";
		break;
		
	case HW_XIRCOMM:
		return "XIRCOMM";
		break;

	case HW_USBMODULE:
		return "USB";
		break;      
		
	default:
		return "Unknown";
		break;  
	}
}


static inline s32
bt_paranoia_check(struct tty_driver *bt_tty, kdev_t device, const u8 *routine)
{
#ifdef BT_PARANOIA_CHECK
	static const u8 *badmagic =
		"Warning: bad magic number for bluetooth driver struct (%s) in %s\n";
	static const char *badinfo =
		"Warning: null tty_driver struct for (%s) in %s\n";

	if (!bt_tty) {
		printk(badinfo, kdevname(device), routine);
		return 1;
	}
	if (bt_tty->magic != BT_TTY_DRIVER_MAGIC) {
		printk(badmagic, kdevname(device), routine);
		return 1;
	}
#endif
	return 0;
}


void
bt_rfcomm_connection_ready(s32 status)
{
	BT_DRIVER("bt_rfcomm_connection_ready:\n");

	con_status = status;
	wake_up_interruptible(&connect_wq);
}

void bt_sdp_connection_ready(s32 status)
{
	BT_DRIVER("bt_sdp_connection_ready !\n");
	con_status = status;
	wake_up_interruptible(&connect_wq);
}

/************************ MODULE STUFF ****************************************/
#ifdef MODULE
s32
init_module(void)
{
	/* FIXME - check if serial module is loaded */
	return bt_init();
}

void
cleanup_module(void) 
{
	unsigned long flags;
	s32 error;

	save_flags(flags);
	cli();

	if ((error = tty_unregister_driver(&bt_driver)))
		printk("SERIAL: failed to unregister bluetooth driver (%d)\n",
		       error);
	restore_flags(flags);

	/* clear line discipline */
	tty_register_ldisc (N_BT, NULL);

	/* FIXME - Shut down all active tty:s if any */
	
	if (bt_stack_initiated)
		bt_shutdown();
	
#ifdef BT_USE_PROC

	proc_unregister(&proc_root, bt_status.low_ino);
	proc_unregister(&proc_root, bt_internal_info.low_ino);
	proc_unregister(&proc_root, sdp_proc_entry.low_ino);
	proc_unregister(&proc_root, bt_proc_doit.low_ino);

#endif /* BT_USE_PROC */
	
	DSYS("Bluetooth Driver unregistered\n");
}


#endif /* MODULE */
/******************************************************************************/

/****************** END OF FILE bluetooth.c *********************************/
