/* 
 * hci.c -- Implementation of Bluetooth Host Controller Interface 
 *          (UART transport layer)
 *
 * Copyright (C) 2000  Axis Communications AB
 *
 * Mats Friden <mats.friden@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: hci.c,v 1.79 2000/10/27 15:09:00 mattias Exp $
 *
 */

#define __NO_VERSION__ /* don't define kernel_version in module.h */

#ifdef __KERNEL__
#include <linux/malloc.h>
#include <linux/timer.h>

#include <linux/bluetooth/hci.h>
#include <linux/bluetooth/btdebug.h>
#include <linux/bluetooth/bluetooth.h>
#include <linux/bluetooth/l2cap.h>
#include <linux/bluetooth/tcs.h>
#include <linux/bluetooth/btmem.h>
#include <linux/delay.h>
#else /* user mode */
#include <stdlib.h>
#include <string.h>
#include "include/bluetooth.h"
#include "include/hci.h"
#include "include/btdebug.h"
#include "include/l2cap.h"
#include "include/btmem.h"
#include "include/tcs.h"
#endif

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

/* Defines for the debug macros */
#if DEBUG_HCI_QUEUEING
#define D_QUEUE(fmt, args...) printk(HCI_DBG_STR fmt, ## args)
#else
#define D_QUEUE(fmt, args...) 
#endif

#if DEBUG_HCI_INDATA
#define D_INDATA(fmt, args...) printk(fmt, ## args)
#else
#define D_INDATA(fmt, args...)
#endif

/* Defines for the debug macros */
#if DEBUG_HCI_RECEIVING_PROCESS
#define D_REC(fmt, args...) printk(HCI_DBG_STR fmt, ## args)
#else
#define D_REC(fmt, args...)
#endif

#if DEBUG_HCI_SEND_PROCESS
#define D_SND(fmt, args...) printk(HCI_DBG_STR fmt, ## args)
#else
#define D_SND(fmt, args...)
#endif

#if DEBUG_HCI_CTRL
#define D_CTRL(fmt, args...) printk(HCI_DBG_STR fmt, ## args)
#else
#define D_CTRL(fmt, args...)
#endif

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

#if DEBUG_HCI_CMD
#define D_CMD(fmt, args...) printk(HCI_DBG_STR fmt, ## args)
#else
#define D_CMD(fmt, args...)
#endif
		

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


#define SET_UART(x1,x2,x3,x4) (((((x4)&7)<<5) + (((x3)&1)<<4) + \
	(((x2)&1)<<3) + (((x1)&1)<<2)) & 0xfc)

#define SET_BAUD_RATE(x) (27648000/(x))


#define SET_PROTOCOL_MODE(x1,x2,x3,x4,x5,x6) ((((x6)&7)<<5) + (((x5)&1)<<4) + \
					      (((x4)&1)<<3) + (((x3)&1)<<2) + \
					      (((x2)&1)<<1) + ((x1)&1))


#define MAKE_ACL_HDR(hci_hdl,pb,bc,len) (((len & 0xffff) << 16) + \
					 ((bc & 0x3) << 14) + \
					 ((pb & 0x3) << 12) + \
					 (hci_hdl & 0xfff))

#define ACL_LINK 1
#define SCO_LINK 0

/* If using host flow control, this is what we can store in our inbuffers */
#define HCI_ACL_LEN 160
#define HCI_SCO_LEN 255
#define HCI_ACL_NUM 8
#define HCI_SCO_NUM 2

/* How many inbuffers we have got, the size of them, and how many command
   buffers we have got */
#define NBR_OF_HCI_INBUFFERS 7
#define HCI_IN_SIZE 17000 //2048
#define NBR_CMD_BUFS 10

/* Now we will define all the different packet types */
/* FIXME: Maybe we should move these to a separate file */

/* Defines for the packet types */
#define CMD_PKT 0x01
#define ACL_PKT 0x02
#define SCO_PKT 0x03
#define EVENT_PKT 0x04
#define ERROR_PKT 0x05
#define NEG_PKT 0x06

/* Define the different OpCode Group Field (OGF) values */
#define HCI_LC 0x01   /* Link Control Command */
#define HCI_LP 0x02   /* Link Policy Command */
#define HCI_HC 0x03   /* Host Controller and Baseband Commands */
#define HCI_IP 0x04   /* Informational Parameters */
#define HCI_SP 0x05   /* StatusParameters */
#define HCI_TC 0x06   /* Test Commands */
#define MANUFACTURER_SPEC 0x3f

/* Define the different OpCode Command Field (OCF) values */

/* Link control commands (HCI_LC) */
#define INQUIRY 0x01
#define INQUIRY_CANCEL 0x02
#define PERIODIC_INQUIRY_MODE 0x3
#define EXIT_PERIODIC_INQUIRY_MODE 0x4
#define CREATE_CONNECTION 0x05
#define DISCONNECT 0x06
#define ADD_SCO_CONNECTION 0x07
#define ACCEPT_CONNECTION_REQUEST 0x09
#define REJECT_CONNECTION_REQUEST 0x0A
#define LINK_KEY_REQUEST_REPLY 0xB
#define LINK_KEY_REQUEST_NEGATIVE_REPLY 0xC
#define PIN_CODE_REQUEST_REPLY 0xD
#define PIN_CODE_REQUEST_NEGATIVE_REPLY 0xE
#define CHANGE_CONNECTION_PACKET_TYPE 0x0F
#define AUTHENTICATION_REQUESTED 0x11
#define SET_CONNECTION_ENCRYPTION 0x13
#define CHANGE_CONNECTION_LINK_KEY 0x15
#define MASTER_LINK_KEY 0x17
#define REMOTE_NAME_REQUEST 0x19
#define READ_REMOTE_SUPPORTED_FEATURES 0x1B
#define READ_REMOTE_VERSION_INFORMATION 0x1D
#define READ_CLOCK_OFFSET 0x1F

/* Link Policy commands (HCI_LP) */
#define HOLD_MODE 0x1
#define SNIFF_MODE 0x3
#define EXIT_SNIFF_MODE 0x4
#define PARK_MODE 0x5
#define EXIT_PARK_MODE 0x6
#define QOS_SETUP 0x7
#define ROLE_DISCOVERY 0x9 
#define SWITCH_ROLE 0xB
#define READ_LINK_POLICY_SETTINGS 0xC
#define WRITE_LINK_POLICY_SETTINGS 0xD

/* Host-Controller and Baseband Commands (HCI_HC) */
#define RESET 0x03
#define FLUSH 0x08
#define READ_STORED_LINK_KEY 0x0D
#define CHANGE_LOCAL_NAME 0x13
#define WRITE_SCAN_ENABLE 0x1A
#define WRITE_PAGESCAN_ACTIVITY 0x1C
#define WRITE_INQUIRYSCAN_ACTIVITY 0x1E
#define WRITE_AUTHENTICATION_ENABLE 0x20
#define WRITE_CLASS_OF_DEVICE 0x24
#define SET_HOST_CONTROLLER_TO_HOST_FLOW_CONTROL 0x31
#define HOST_BUFFER_SIZE 0x33
#define HOST_NBR_OF_COMPLETED_PACKETS 0x35
#define READ_LINK_SUPERVISON_TO 0x36
#define WRITE_LINK_SUPERVISON_TO 0x37

/* Informational Parameters (HCI_IP) */
#define READ_LOCAL_VERSION_INFORMATION 0x1 
#define READ_LOCAL_SUPPORTED_FEATURES 0x3
#define READ_BUFFER_SIZE 0x05
#define READ_COUNTRY_CODE 0x7
#define READ_BD_ADDR 0x09

/* Status Parameters (HCI_SP) */
#define READ_FAILED_CONTACT_COUNTER 0x1
#define RESET_FAILED_CONTACT_COUNTER 0x2
#define GET_LINK_QUALITY 0x3
#define READ_RSSI 0x5

/* Testing commands (HCI_TC) */
#define READ_LOOPBACK_MODE 0x01
#define WRITE_LOOPBACK_MODE 0x02
#define ENABLE_DEVICE_UNDER_TEST_MODE 0x03

/* Vendor-specific debug commands */

/* For Ericsson */
#define ERICSSON_SET_UART_BAUD_RATE 0x9
#define ERICSSON_WRITE_BD_ADDR 0xd
#define ERICSSON_READ_REVISION_INFORMATION 0xF
#define ERICSSON_ENTER_TEST_MODE 0x11
#define ERICSSON_TEST_CONTROL 0x12
#define ERICSSON_TX_TEST 0x19

/* Defines of the different events that is sent from the Host Controler
   to the Host */
#define INQUIRY_COMPLETE 0x01
#define INQUIRY_RESULT 0x02
#define CONNECTION_COMPLETE 0x03
#define CONNECTION_REQUEST 0x04
#define DISCONNECTION_COMPLETE 0x05
#define AUTHENTICATION_COMPLETE 0x6
#define REMOTE_NAME_REQUEST_COMPLETE 0x7
#define ENCRYPTION_CHANGE 0x8
#define CHANGE_CONNECTION_LINK_KEY_COMPLETE 0x9
#define MASTER_LINK_KEY_COMPLETE 0xA
#define READ_REMOTE_SUPPORTED_FEATURES_COMPLETE 0xB
#define READ_REMOTE_VERSION_INFORMATION_COMPLETE 0xC
#define QOS_SETUP_COMPLETE 0xD
#define COMMAND_COMPLETE 0x0E
#define COMMAND_STATUS 0x0F
#define HARDWARE_ERROR 0x10
#define FLUSH_OCCURRED 0x11
#define ROLE_CHANGED 0x12
#define NBR_OF_COMPLETED_PACKETS 0x13
#define MODE_CHANGE 0x14
#define RETURN_LINK_KEYS 0x15
#define PIN_CODE_REQUEST 0x16
#define LINK_KEY_REQUEST_EVENT 0x17
#define LINK_KEY_NOTIFICATION 0x18
#define LOOPBACK_COMMAND 0x19
#define DATA_BUFFER_OVERFLOW 0x1A
#define MAX_SLOTS_CHANGE 0x1B
#define READ_CLOCK_OFFSET_COMPLETE 0x1C
#define CONNECTION_PACKET_TYPE_CHANGED 0x1D
#define QOS_VIOLATION 0x1E
#define PAGE_SCAN_MODE_CHANGE 0x1F
#define PAGE_SCAN_REPETITION_MODE_CHANGE 0x20

/* The flags in the HCI header */
#define L2CAP_FRAME_START 0x02
#define L2CAP_FRAME_CONT 0x01

#define HCI_HDR_LEN 1
#define ACL_HDR_LEN 4
#define SCO_HDR_LEN 3
#define CMD_HDR_LEN 3
#define EVENT_HDR_LEN 2

/* Special events */
#define DIGI_EVENT 0xFF /* Digianswer Events */

#define DM1 0x0008
#define DM3 0x0400
#define DM5 0x4000
#define DH1 0x0010
#define DH3 0x0800
#define DH5 0x8000

#define HCI_SYNC_FIX

#ifdef __KERNEL__
#define HCI_RCV_TIMER 0
#endif

#ifdef HCI_EMULATION

/* Ericsson HW */
#define ACL_NUM 10
#define ACL_LEN 800

#define SCO_NUM 2
#define SCO_LEN 100

#define HCI_HDL 0
#define ACL_CON 0x1
#endif

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

/* Struct defining a HCI command packet, the meaning of the type field is
   decribed in part H:4 chapter 2 in the baseband core specification. The
   other fields are described in section H:1 chapter 4.4.1 in the baseband
   core specification */ 

typedef struct cmd_pkt {
	u32 type:8;
        u32 ocf:10;
        u32 ogf:6;
        u32 len:8;
        
        u8 data[256];
} cmd_pkt;

/* Struct used to keep track of the current number of buffers, and the sizes
   of the buffers in the bluetooth module. There are two buffers for data in
   the modul, ACL buffers for ordinary data and SCO buffers for audion data.
   The cmd_num, describes how many more command packets that can be sent to
   the module. */

typedef struct host_controller_buffers{
	s32 acl_len;
	s32 sco_len;
	s32 acl_num;
	s32 sco_num;
	s32 cmd_num;
} host_controller_buffers;

/* The hci_in_buffer is used for storing hci packtes until a complete L2CAP
   packet has been received. When a complete L2CAP packet is received and
   processed by the L2CAP layer the empty variable is set to TRUE */

typedef struct hci_in_buffer {
	u8 *buf_ptr;
	u32 count;
	u32 empty;
	u32 hci_hdl;
	u32 nbr_of_hci_pkt;
	u32 l2cap_len;
	u8 buf[HCI_IN_SIZE];
} hci_in_buffer;

enum bt_states{UNIT_ACTIVE, UNIT_PASSIVE, NOT_CONNECTED};

typedef struct hci_con {
	enum bt_states state;
	u8 bd[6];
	u16 con_hdl;
	u8 name[248];
} hci_con;

/* Used as a control object for the entire HCI layer. The trash_buffer is
   only used as an emergency buffer to avoid a program crash if something has
   gone wrong with the hci_in_buffers */

/* FIXME: Do we really need the trash_buffer? */

typedef struct hci_controller {
	host_controller_buffers hc_buf;
	hci_in_buffer hci_in_buf[NBR_OF_HCI_INBUFFERS];
	hci_in_buffer trash_buffer;
	u32 nbr_of_connections;
	u8 local_bd[6];
	hci_con con[MAX_NBR_OF_CONNECTIONS];
} hci_controller;

/* cmd_t and cmd_buf are structures used to handle the queue of commands
   waiting to be sent on the serial port */

typedef struct cmd_t {
	u8* data;
	u8 len; /* max 255 */
} cmd_t;

struct cmd_buf {
	u32 first_free; /* index in cmd_buf */
	u32 next_to_send; /* index in cmd_buf */
	u8 count;
	cmd_t buf[NBR_CMD_BUFS];
} cmd_buf;

/* This is the states the state machine in hci_receive_data can be in */
enum states{WAIT_FOR_PACKET_TYPE, WAIT_FOR_EVENT_TYPE, WAIT_FOR_EVENT_LENGTH,
            WAIT_FOR_EVENT_PARAM, WAIT_FOR_ACL_HDR, WAIT_FOR_ACL_DATA, 
            WAIT_FOR_SCO_HDR, WAIT_FOR_SCO_DATA, WAIT_FOR_TIMEOUT};

#ifdef HCI_EMULATION

/* If we are using the HCI emulation, we need a definition of the event packet
   to, see part H:1 chapter 4.4.2 of the baseband core specification for a
   description of the HCI event packet. */

typedef struct event_struct {
	u32 pkt_type:8;
	u32 event_type:8;
	u32 len:8;
	u8 data[0];
} event_struct;
#endif

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

static void process_event(u8 *buf, u32 len, u32 event_code);
static void process_return_param(u8 *buf);
static void process_acl_data(hci_in_buffer* in_buf, u32 pb_flag);
static void process_sco_packet(u8 *data, u32 hci_hdl, u32 len);
static hci_in_buffer* get_inbuffer(u32 hci_hdl);
static hci_in_buffer* get_free_inbuffer(void);
static s32 send_cmd(u8 *cmd, u8 len);
static u32 send_acl_packet(bt_tx_buf *tx_buf);
static void set_acl_hdr(u8 *data, u32 len, u8 pb, u8 bc, u32 hci_hdl);
u8* get_digi_err_code(u8 code);
static cmd_t* get_next_cmd(void); 
static s32 insert_cmd(u8* cmd, u8 len); 
static void init_cmd_buf(void);
static s32  send_cmd_queue(void);
static void start_cmd_timer(void);
static void release_cmd_timer(void);

#ifdef __KERNEL__
static void hci_cmd_timeout(unsigned long ptr);
#endif
static s32 accept_connection_request(u8 bd_addr[], u8 role);
static s32 change_connection_packet_type(u32 hci_hdl, u32 pkt_type);
static s32 create_connection(u8 bd_addr[], u32 pkt_type, u8 psrm, u8 psm, u32 c_off, u32 rol_sw);
static s32 disconnect(u32 hdl, u8 reason);

#ifdef HOST_FLOW_CTRL
static s32 host_nbr_completed_packets(u32 hci_hdl, u32 nbr_of_packets);
static s32 host_buffer_size(u16 acl_len, u8 sco_len, u16 acl_num, u16 sco_num);
static s32 set_host_controller_flow_control(u32 enable);
#endif

#if 0 /* FIXME */
static s32 read_stored_link_key(u8 *bd, u8 flag);
static s32 write_inquiryscan_activity(u32 interval, u32 wind);
static s32 write_authentication_enable(u32 enable);
static s32 send_negotiation_pkt(void);
static s32 read_link_to(u32 hdl);
static s32 flush(u32 hdl);
#endif

static s32 reject_connection_request(u8 bd_addr[], u32 reason);
static s32 read_buffer_size(void);

static s32 read_ericsson_rev_info(void);
static s32 write_class_of_device(u8 minor, u8 major, u16 service_class);

static s32 remote_name_request(u8 *bd);
static s32 link_key_request_reply(u8 *bd, u8* link_key);
static s32 pin_code_request_reply(u8 *bd, u8 *pin, u8 pin_len);

static void set_hci_con(u8 *bd, s32 con_hdl);
static void set_hci_con_name(u8 *bd, u8 *name);
static void reset_hci_con_bd(u16 con_hdl);

#if HCI_RCV_TIMER
static void hci_rec_data_timeout(unsigned long ptr);
#endif

#ifdef HCI_EMULATION
static void hci_emulator(u8 *data, u32 len);
static void command_handler(u8 *data);
static s32 set_cmd_status_event(event_struct *event, u8 *cmd);
static s32 set_con_req_event(event_struct *event, u8 *bd, u8 con_type);
static s32 set_con_cpl_event(event_struct *event, u8 status, u8 *bd,
	u16 hci_hdl,u8 con_type);
static s32 set_discon_cpl_event(event_struct *event, u16 hci_hdl, u8 reason);
#endif

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

/* Struct used for sending command packets */
cmd_pkt c_pkt;

/* The states for the switch-statement in hci_receive_data fuction */
static enum states state;

/* General HCI controller stuct, contains vital information about buffer 
   sizes and conections */
static hci_controller hci_ctrl;

#ifdef __KERNEL__
struct timer_list hci_cmd_timer;
struct timer_list hci_receive_timer;
s32 rcv_timers = 0;
#endif

struct wait_queue *hci_wq = NULL;

struct wait_queue *inq_wq = NULL;

struct wait_queue *set_baudrate_wq = NULL;

struct wait_queue *test_wq = NULL;

static struct inquiry_results *inq_res = NULL;

s32 bt_current_hw = HW_DEFAULT;

/* used to differentiate between different HW setups */
s32 current_hw;

static s32 test_hci_hdl;

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

#if HCI_RCV_TIMER
void
set_rec_timer(void)
{
	/* Max 1 outstanding timer */
	if (rcv_timers == 0) {
		printk("Starting hci_receive timer (1 sec)\n");
		
		init_timer(&hci_receive_timer);
		hci_receive_timer.function = hci_rec_data_timeout;
		hci_receive_timer.data = 0;
		hci_receive_timer.expires = jiffies + HZ;
		rcv_timers++;
		add_timer(&hci_receive_timer);
		
		/* issue a test command in time out function */
		state = WAIT_FOR_TIMEOUT;
	}
}
#endif

/*
 * Parses the serial data and sends it to either the parse_acl_packet, 
 * parse_sco_packet or the parse_event_packet, depending on the packet type
 */

void
hci_receive_data(u8* data, u32 count)
{
#define FNC "hci_receive_data: "
	static u32 event_len;
	static u32 data_len;
	static u32 event_type;
	static u32 pb_flag;
	static u32 bc_flag;
	static u32 hci_hdl;
	static u8 event_buf[256];
	static u8 hdr[4];
	static hci_in_buffer *in_buf;
	u32 c;     /* temporary variable for index calculations */ 
  
	static u32 tmp_pos;
	u8 *buf; /* Temporary pointer to the incoming data */
	u32 tmp_data_len;
	s32 syncing = 1;
 
	PRINTPKT("hci_receive_data : ", data, count);
  
	tmp_data_len = count;
	buf = data;

	/* Here we get a chunk of data from the serial port. When a complete 
	   packet is received it is copied into a buffer and sent to the 
	   processing function */
  
	D_INDATA("hci-%d\n", tmp_data_len);
	while (count > 0) {
		switch (state) {
			
		/* The first byte will tell us whether it is an event or a data
		   packet */
		case WAIT_FOR_PACKET_TYPE:
			D_REC(FNC"WAIT_FOR_PACKET_TYPE\n");
			tmp_pos = 0;
			if (*buf == EVENT_PKT) {
				state = WAIT_FOR_EVENT_TYPE;
				buf += 1;
				count -= 1;
			} else if (*buf == ACL_PKT) {
				state = WAIT_FOR_ACL_HDR;
				buf += 1;
				count -= 1;
			} else if (*buf == SCO_PKT) {
				state = WAIT_FOR_SCO_HDR;
				buf += 1;
				count -= 1;
			} else {

				D_ERR("Bad UART baud rate or trashed data on the uart\n");
				D_ERR("Try reducing uart speed or change IRQ setting (PC)\n");

				if (syncing) {  
					printk("Resetting state machine and trying to resync\n");
					syncing = 0;
					buf = data;
					count = tmp_data_len;
					state = WAIT_FOR_PACKET_TYPE;
				} else {
					state = WAIT_FOR_TIMEOUT;
					count = 0;
#if HCI_RCV_TIMER
					set_rec_timer();
#endif
				}
			}  
			break;

			/* If it is an event packet we wait for the next byte
			   which tell us which kind of event it is */ 
		case WAIT_FOR_EVENT_TYPE:
			D_REC(FNC"WAIT_FOR_EVENT_TYPE\n");
			event_type = *buf;
			state = WAIT_FOR_EVENT_LENGTH;
			buf += 1;
			count -= 1;  
			break;

			/* If it is an event the length-field is one byte */
		case WAIT_FOR_EVENT_LENGTH:
			D_REC(FNC"WAIT_FOR_EVENT_LENGTH\n");
			event_len = *buf;
			buf += 1;
			count -= 1;
			if (event_len <= count) {
				process_event(buf, event_len, event_type);
				buf += event_len;
				count -= event_len;
				state = WAIT_FOR_PACKET_TYPE;
			} else {
				state = WAIT_FOR_EVENT_PARAM;
			}
			break;

			/* Before we can parse the event, we wait for the
			   whole event */
		case WAIT_FOR_EVENT_PARAM:
			D_REC(FNC"WAIT_FOR_EVENT_PARAM\n");
			if (tmp_pos < event_len) {
				c = MIN(count, event_len - tmp_pos);
				memcpy(event_buf + tmp_pos, buf, c);
				tmp_pos += c;
				count -= c;
				buf += c;
				if (tmp_pos == event_len) {
					process_event(event_buf,event_len,
						      event_type);
					state = WAIT_FOR_PACKET_TYPE;
				}
			}
			break;

			/* Here we wait for the whole data header, four bytes*/
		case WAIT_FOR_ACL_HDR:
			D_REC(FNC"WAIT_FOR_ACL_HDR\n");
			c = MIN(count,ACL_HDR_LEN - tmp_pos);
			memcpy(hdr + tmp_pos, buf, c);
			tmp_pos += c;
			buf += c;
			count -= c;
			if (tmp_pos < ACL_HDR_LEN) {

			} else if (tmp_pos == ACL_HDR_LEN) {
				hci_hdl = CHAR2INT12(hdr[1],hdr[0]);
				pb_flag = (((u32) hdr[1]) & 0x30) >> 4;
				bc_flag = (((u32) hdr[1]) & 0xc0) >> 6;
				data_len = CHAR2INT16(hdr[3],hdr[2]);

				if (pb_flag == L2CAP_FRAME_START) {
					D_REC(FNC"new frame\n");
					in_buf = get_free_inbuffer();
					in_buf->nbr_of_hci_pkt = 1;
					in_buf->hci_hdl = hci_hdl;
				} else {
					D_REC(FNC"cont frame\n");
					in_buf = get_inbuffer(hci_hdl);
					in_buf->nbr_of_hci_pkt += 1;
				}
				/* When we change state we reset data_index,
				   because we are using separate buffers for
				   the HCI header and the HCI data */
				tmp_pos = 0;
				state = WAIT_FOR_ACL_DATA;
			}
			break;

			/* Finally we process the data, i.e we wait for the
			   whole data packet and then we send it to a higher
			   protocol layer, in this case the L2CAP-layer */
		case WAIT_FOR_ACL_DATA:
			D_REC(FNC"WAIT_FOR_ACL_DATA\n");
			/* Find out how much data we can copy, don't copy more
			   than one HCI packet at time */
			c = MIN(count, data_len - tmp_pos);
	
			memcpy(in_buf->buf_ptr, buf, c);
			in_buf->buf_ptr += c;
			in_buf->count += c;
			D_REC(FNC"in_buf->count = %d\n",
			      in_buf->count);
      
			/* Increase the data_index and decrease the amount of
			   data */
			tmp_pos += c;
			count -= c;
      
			D_REC(FNC"Copied %d bytes into inbuffer\n",c);
			if (tmp_pos == data_len) {
#ifdef HOST_FLOW_CTRL        
				/* send host_nbr_of_completed_packets to the
				   host */
				host_nbr_completed_packets(in_buf->hci_hdl, 1);
#endif

				process_acl_data(in_buf, pb_flag);
				state = WAIT_FOR_PACKET_TYPE;
			}
			buf += c;
			break;
      
			/* wait for the SCO header, three bytes */
		case WAIT_FOR_SCO_HDR:
			D_REC(FNC"WAIT_FOR_SCO_HDR\n");
			if (tmp_pos < SCO_HDR_LEN) {
				c = MIN(count,SCO_HDR_LEN - tmp_pos);
				memcpy(hdr + tmp_pos, buf, c);
				tmp_pos += c;
				buf += c;
				count -= c;
				if (tmp_pos == SCO_HDR_LEN) {
					hci_hdl = CHAR2INT12(hdr[1],hdr[0]);
					data_len = hdr[2];
					tmp_pos = 0;
					state = WAIT_FOR_SCO_DATA;
				}
			}
			break;

			/* Wait for the rest of the SCO packet and send it to
			   the tty */
		case WAIT_FOR_SCO_DATA:
			D_REC(FNC"WAIT_FOR_SCO_DATA\n");
			c = MIN(count, data_len - tmp_pos);
			process_sco_packet(buf, hci_hdl, c);
			tmp_pos += c;
			buf += c;
			count -= c;
			if (tmp_pos == data_len) {
				state = WAIT_FOR_PACKET_TYPE;
        
				/* FIXME - Send the data to the audio tty */

			}
			break;

		case WAIT_FOR_TIMEOUT:
			/* Discard all incoming data */
#if HCI_RCV_TIMER
			/* start a new timer ? */			
			D_ERR("Waiting for timeout, discarding %d bytes...\n", count);
#else
			D_ERR("Discarding %d bytes and waiting forever...\n", count);
#endif
			count = 0;
			break;

		default:
			/* Discard all incoming data */
			count = 0;
			break;
		}
	}
#undef FNC
}

void update_ncp(u8 nbr_of_hdl, u8 *pkt)
{
#define FNC "update_ncp: "
	s32 i;
	bt_tx_buf* tx_buf;

	D_REC(FNC"acl_num before : %d\n", hci_ctrl.hc_buf.acl_num);
	
	/* FIXME */
	/* Check if we have to register which connection handlers that
	   have been sent, or if we just need to know how many packets
	   that have been sent */

	for (i = 0; i < 4 * nbr_of_hdl; i += 4) {
		hci_ctrl.hc_buf.acl_num += CHAR2INT16(pkt[i+3], pkt[i+2]);
	}
	D_REC(FNC"acl_num after : %d\n", hci_ctrl.hc_buf.acl_num);
	
	/* while there are space in the hardware buffers and we have
	   hci packets to send, we send them. After each sent packet
	   we assume that the space in the hardware buffers have
	   decreased by one and checks whether there are more packets
	   to send. */
	
	tx_buf = get_bt_buf();

	while ((hci_ctrl.hc_buf.acl_num > 0) && tx_buf) {
		D_SND(FNC"Sending HCI packet\n");
		hci_ctrl.hc_buf.acl_num--;

		send_acl_packet(tx_buf);
		tx_buf = get_bt_buf();
		D_QUEUE("<--%d (%d)\n", buf_byte_count(), 
			hci_ctrl.hc_buf.acl_num);
	}

	/* Now, either our buffer is emptied or HW buffers
	   is busy, if our buffer is empty and HW buffers 
	   is not busy check if there are more to send from 
	   tty driver 
	*/

	if (buf_count()) {
		/* still data to send */
		if (!tx_buf)
			D_ERR(FNC"get_bt_buf failed!\n");
		else if (hci_ctrl.hc_buf.acl_num)
			D_ERR(FNC"we can send but we don't !\n");
	} else {
		/* buffers emptied, check if more data is waiting to
		   be sent in upper tty:s */
#ifdef __KERNEL__
		bt_feedstack();
#endif
	}
	
#undef FNC
}

/*
 * Parses an eventpacket, buf is the event parameters, length the event length
 * and event_code is the event's opcode
 */
void
process_event(u8 *buf, u32 len, u32 event_code)
{
#define FNC "process_event: "
	bt_tx_buf* tx_buf;
	u32 hci_hdl;
	s32 i;
	PRINTPKT(FNC,buf,len);

	switch (event_code) {
	case INQUIRY_COMPLETE:
		printk("INQUIRY_COMPLETE\n");
		wake_up_interruptible(&inq_wq);
		break;

	case INQUIRY_RESULT:
	{
		s32 i, j;
		u8 tmp_bd[6];

		printk(FNC"INQUIRY_RESULT\n");
		for(i = 1; i < (buf[0] * 14) + 1; i += 14) {		
			printk("BD addr %d\n", i % 14);
			
			for (j = 0; j < 6; j++) {
				tmp_bd[5-j] = buf[i + j];
			}
			print_data("BD",tmp_bd, 6);
			memcpy(inq_res->bd_addr + inq_res->nbr_of_units * 6,
			       tmp_bd, 6);
			inq_res->nbr_of_units += 1;
		}
		break;
	}

	case CONNECTION_COMPLETE:
	{
		u8 link_type;

		D_REC(FNC"CONNECTION_COMPLETE: %s\n",
		      get_err_msg(buf[0]));

		hci_ctrl.nbr_of_connections += 1;

		link_type = buf[9];
		hci_hdl = CHAR2INT12(buf[2], buf[1]);
		test_hci_hdl = hci_hdl;

  		if (link_type == ACL_LINK) {
  			lp_connect_cfm(buf + 3, (u32) buf[0], hci_hdl);
  		} else {
  			tcs_add_sco_link(buf[0], hci_hdl);
  		}

#ifndef HCI_EMULATION
		if (!buf[0]) {
			change_connection_packet_type(hci_hdl, DH1|DM1|DH3
						      |DM3|DM5|DH5);
		}
#endif /* HCI_EMULATION */
		set_hci_con(buf + 3, hci_hdl);
		remote_name_request(buf + 3);
		break;
	}
	case CONNECTION_REQUEST:
		D_REC(FNC"CONNECTION_REQUEST\n");

		if (buf[9] == ACL_LINK) {
			lp_connect_ind(buf); /* BD_ADDRESS */
		} else {
			printk("CONNECTION_REQUEST for SCO LINK");
			accept_connection_request(buf, 0x01);
		}
		break;

	case DISCONNECTION_COMPLETE:

		D_REC(FNC"DISCONNECTION_COMPLETE %s\n", get_err_msg(buf[3]));

		lp_disconnect_ind(CHAR2INT12(buf[2], buf[1]));
		/* FIXME: No more NBR_OF_COMPLETE_PACKETS will arrive for this
		   connection handle, if we only support point-to-point
		   connections we can acl_num by reading the buffersizes again,
		   but this will not work in a multipoint connection. */
		hci_ctrl.nbr_of_connections -= 1;

		reset_hci_con_bd(CHAR2INT12(buf[2], buf[1]));
		
		if (hci_ctrl.nbr_of_connections <= 0) {
			read_buffer_size();
		}
		break;

	case AUTHENTICATION_COMPLETE:
		DSYS(FNC"AUTHENTICATION_COMPLETE Not implemented!\n");
		break;

	case REMOTE_NAME_REQUEST_COMPLETE:
		printk(FNC"REMOTE_NAME_REQUEST_COMPLETE %s\n", buf + 7);
		if (buf[0]) {
			D_ERR("REMOTE_NAME_REQUEST_COMPLETE: %s\n",
			      get_err_msg(buf[0]));
		} else {
			if (strlen(buf + 7) > 248) {
				D_ERR("REMOTE_NAME_REQUEST_COMPLETE: too long name length %d\n", strlen(buf + 7));
				break;
			}
			set_hci_con_name(buf + 1, buf + 7);
		}
		break;

	case ENCRYPTION_CHANGE:
		DSYS(FNC"ENCRYPTION_CHANGE Not implemented!\n");
		break;

	case CHANGE_CONNECTION_LINK_KEY_COMPLETE:
		DSYS(FNC"CHANGE_CONNECTION_LINK_KEY_COMPLETE Not implemented!\n");
		break;

	case MASTER_LINK_KEY_COMPLETE:
		DSYS(FNC"MASTER_LINK_KEY_COMPLETE Not implemented!\n");
		break;

	case READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
		DSYS(FNC"READ_REMOTE_SUPPORTED_FEATURES_COMPLETE Not implemented!\n");
		break;

	case READ_REMOTE_VERSION_INFORMATION_COMPLETE:
		DSYS(FNC"READ_REMOTE_VERSION_INFORMATION_COMPLETE Not implemented!\n");
		break;

	case QOS_SETUP_COMPLETE:
		DSYS(FNC"QOS_SETUP_COMPLETE Not implemented!\n");
		break;

	case COMMAND_COMPLETE:
		D_REC(FNC"COMMAND_COMPLETE\n ");
		hci_ctrl.hc_buf.cmd_num += buf[0];

		/* FIXME - stop any outstanding send timers if cmd_num is > 0*/
		D_QUEUE("<CC>\n");
		D_REC(FNC"now cmd_num = %d\n ", hci_ctrl.hc_buf.cmd_num);

		process_return_param(buf);
		send_cmd_queue();
		break;

		/* used to prevent host from waiting on reply when a 
		   command has been received in HW and is currently 
		   performing the task for this command */
	case COMMAND_STATUS: 
		D_REC(FNC"COMMAND_STATUS\n");

		if (buf[0]) {
			D_ERR("COMMAND_STATUS: %s\n",get_err_msg(buf[0]));
		}

		hci_ctrl.hc_buf.cmd_num =+buf[1];
		D_REC("now cmd_num = %d\n ", hci_ctrl.hc_buf.cmd_num);
		send_cmd_queue();
		break;

	case FLUSH_OCCURRED:
		D_REC(FNC"FLUSH_OCCURRED on hci_hdl %d\n",
		      CHAR2INT12(buf[0],buf[1]));

		if ((tx_buf = get_bt_buf())) {
			if ((tx_buf->pb_flag == L2CAP_FRAME_CONT)
			    && (tx_buf->hci_hdl == CHAR2INT12(buf[0],buf[1]))){
				printk("FLUSH_OCCURRED, continious fragment\n");
				unsubscribe_bt_buf(tx_buf);
			}
		}
		break;

	case HARDWARE_ERROR:
		D_ERR("An hardware error with error code 0x%02x occurred\n Please refer to your bluetooth module manual\n", buf[0]);
		break;

	case ROLE_CHANGED:
		if (buf[0]) {
			D_ERR("Role changed faild due to %s\n",
			      get_err_msg(buf[0]));
		} else if (buf[7]) {
			DSYS("Role was changed to Slave for unit with BD Address 0x%04x%08x\n", CHAR2INT16(buf[6], buf[5]), CHAR2INT32(buf[4], buf[3], buf[2], buf[1]));
		} else {
			DSYS("Role was changed to Master for unit with BD Address 0x%04x%08x\n", CHAR2INT16(buf[6], buf[5]), CHAR2INT32(buf[4], buf[3], buf[2], buf[1]));
		}
		break;

	case NBR_OF_COMPLETED_PACKETS: 

		D_REC(FNC"NUMBER_OF_COMPLETED_PACKETS\n");
	
		update_ncp(buf[0], buf + 1);

		D_QUEUE("<NCP:%d>\n",hci_ctrl.hc_buf.acl_num);

		break;

	case MODE_CHANGE:
		D_REC(FNC"MODE_CHANGE on hci_hdl %d\n",
		      CHAR2INT12(buf[2],buf[1]));
		break;

	case RETURN_LINK_KEYS:
		D_REC(FNC"RETURN_LINK_KEYS event\n");
		for (i = 1; i <= buf[0]; i++) {
			printk("%d: bd_addr %02x:%02x:%02x:%02x%02x:%02x\n",
			      i, buf[6*i], buf[5*i], buf[4*i], buf[3*i],
			      buf[2*i], buf[1*i]);
		}
		for (i = 1; i <= buf[0]; i++) {
			printk("%d: ",i);
			print_data("Link key", buf + 7 + (i * 16), 16);
		}
		break;

	case PIN_CODE_REQUEST:
	{
		u8 tmp_code[16];
		s32 i;
		for (i = 0; i < 16; i++) {
			tmp_code[i] = 0;
		}

		D_REC(FNC"PIN_CODE_REQUEST event\n");

		pin_code_request_reply(buf, tmp_code, 4); 
		break;
	}
	case LINK_KEY_REQUEST_EVENT:
	{
		u8 link_key[16];
		printk(FNC"LINK_KEY_REQUEST_EVENT\n");

		memset(link_key, 0, 16);
		link_key_request_reply(buf, link_key);
	}
	break;

	case LINK_KEY_NOTIFICATION:
		DSYS(FNC"LINK_KEY_NOTIFICATION Not implemented\n");
		break;

	case LOOPBACK_COMMAND:
		DSYS(FNC"LOOPBACK_COMMAND, loopback mode not supported\n");
		break;

	case DATA_BUFFER_OVERFLOW:
		printk(FNC"DATA_BUFFER_OVERFLOW\n");
		break;

	case MAX_SLOTS_CHANGE:
		D_REC(FNC"MAX_SLOTS_CHANGE to %d\n",
		      (u32) buf[2]);
		break;

	case READ_CLOCK_OFFSET_COMPLETE:
		D_REC(FNC"READ_CLOCK_OFFSET_COMPLETE\n");
		break;

	case CONNECTION_PACKET_TYPE_CHANGED:
		D_REC(FNC"CHANGE_CONNECTION_PACKET_TYPE\n");
		break;

	case QOS_VIOLATION:
		D_REC(FNC"QOS_VIOLATION\n");
		break;

	case PAGE_SCAN_MODE_CHANGE:
		DSYS(FNC"PAGE_SCAN_MODE_CHANGE Not implemented\n");
		break;

	case PAGE_SCAN_REPETITION_MODE_CHANGE:
		DSYS(FNC"PAGE_SCAN_REPETITION_MODE_CHANGE Not implemented\n");
		break;	

	case DIGI_EVENT:
		D_REC(FNC"DIGIANSWER EVENT\n");
		if (len == 0x01) {
			D_REC("DIGIANSWER RESET REQUEST EVENT\n");
		} else {			
			printk(get_digi_err_code(buf[0]));
		}
		break;

	default:
		DSYS(FNC"UNKNOWN EVENT CODE 0x%x\n",event_code);
		break;
	}
#undef FNC
}

u8* get_digi_err_code(u8 code)
{
	switch (code) {
	case 0x01:
		return "The data packet is too large, No valid packet types\n";

	case 0x02:
		return "The data packet is too large, Max slots exceeded\n";

	case 0x10:
		return "No connection for data. The connection handle specified by the data packet is not in use\n";

	case 0x20:
		return "The UCP2 synchronization signal is not right\n";

	case 0xFF:
		return "Unspecified error\n";

	default:
		return "Unkown error digianswer code";
	}
}

/* Parses the return parameters from the command complete event, see the
   bluetooth baseband specification part H:1, chapter 5.2.14 for a further
   description of the command complete event. */

void 
process_return_param(u8 *buf)
{
#define FNC "process_return_param: "
	u32 ogf,ocf;
	u8 *r_val;

	/* buf points at the start of the event parameters of the command
	   complete event. The second and third byte (buf[1] and buf[2])
	   contains the op-code, which consists of the OGF and the OCF values,
	   see the baseband specification part H:1, chapter 4.4.1 for a
	   description of these.

	   r_val is set to point at the beginning of the return parameters at
	   the command complete event */

	ogf = (u32) buf[2] >> 2;
	ocf = CHAR2INT10(buf[2],buf[1]);
	r_val = buf + 3;

	switch (ogf) {

	case HCI_LC:
		switch (ocf) {
		case LINK_KEY_REQUEST_REPLY:
		{
			D_REC(FNC"LINK_KEY_REQUEST_REPLY\n");
			if (r_val[0]) {
				D_ERR("LINK_KEY_REQUEST_REPLY %s\n",
				      get_err_msg(r_val[0]));
			}
			else {
				printk("LINK_KEY_REQUEST_REPLY Success\n");
			}
		}
		break;

		case PIN_CODE_REQUEST_REPLY:
			D_REC(FNC"PIN_CODE_REQUEST_REPLY\n");
			if (r_val[0]) {
				D_ERR("PIN_CODE_REQUEST_REPLY %s\n",
				      get_err_msg(r_val[0]));
			}
			else {
				printk("PIN_CODE_REQUEST_REPLY  Success\n");
			}
		break;

		default:
			DSYS(FNC"OCF value 0x%x not fond for link control command\n",
			     ocf);
			break;
		}
		break;

	case HCI_HC:                 /* Host Controller commands */
		switch (ocf) {
		case READ_STORED_LINK_KEY:
			D_REC(FNC"READ_STORED_LINK_KEY\n");
			if (r_val[0]) {
				D_ERR("READ_STORED_LINK_KEY %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			printk("Max number of Linkkeys: %d\n",
			      CHAR2INT16(r_val[2], r_val[1]));
			printk("Number of Linkkeys read: %d\n",
			      CHAR2INT16(r_val[4], r_val[3]));
			break;

		case SET_HOST_CONTROLLER_TO_HOST_FLOW_CONTROL:
			D_REC(FNC"SET_HOST_CONTROLLER_TO_HOST_FLOW_CONTROL\n");
			release_cmd_timer();

			if (r_val[0]) {
				D_ERR("SET_HOST_CONTROLLER_TO_HOST_FLOW_CONTROL: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			wake_up_interruptible(&hci_wq);
			break;

		case HOST_BUFFER_SIZE:

			D_REC(FNC"HOST_BUFFER_SIZE\n");
			release_cmd_timer();
			if (r_val[0]) {
				D_ERR("HOST_BUFFER_SIZE: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}

			wake_up_interruptible(&hci_wq);
			break;

		case CHANGE_LOCAL_NAME:
			D_REC(FNC"CHANGE_LOCAL_NAME\n");
			release_cmd_timer();

			if (r_val[0]) {
				D_ERR("CHANGE_LOCAL_NAME: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			wake_up_interruptible(&hci_wq);
			break;

		case WRITE_SCAN_ENABLE:
			D_REC(FNC"WRITE_SCAN_ENABLE\n");
			release_cmd_timer();
			if (r_val[0]) {
				D_ERR("WRITE_SCAN_ENABLE: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}

			wake_up_interruptible(&hci_wq);

			break;

		case WRITE_PAGESCAN_ACTIVITY:
			D_REC(FNC"WRITE_PAGESCAN_ACTIVITY\n");
			if (r_val[0]) {
				D_ERR("WRITE_PAGESCAN_ACTIVITY: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			break;

		case WRITE_INQUIRYSCAN_ACTIVITY:
			D_REC(FNC"WRITE_INQUIRYSCAN_ACTIVITY\n");
			if (r_val[0]) {
				D_ERR("WRITE_INQUIRYSCAN_ACTIVITY: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			break;

		case WRITE_AUTHENTICATION_ENABLE:
			D_REC(FNC"WRITE_AUTHENTICATION_ENABLE\n");
			release_cmd_timer();
			if(r_val[0]) {
				D_ERR("WRITE_AUTHENTICATION_ENABLE: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			wake_up_interruptible(&hci_wq);
			break;

		case WRITE_CLASS_OF_DEVICE:
			D_REC(FNC"WRITE_CLASS_OF_DEVICE\n");
			if(r_val[0]) {
				D_ERR("WRITE_CLASS_OF_DEVICE: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			break;

		case READ_LINK_SUPERVISON_TO:
			printk("Link supervision timeout is: %d\n",
			      CHAR2INT12(buf[4],buf[3]));
			break;

		case WRITE_LINK_SUPERVISON_TO:
			printk(FNC"WRITE_LINK_SUPERVISON_TO (%d)\n", r_val[0]);
			if (r_val[0]) {
				D_ERR("WRITE_LINK_SUPERVISON_TO: %s\n",
				      get_err_msg(r_val[0]));
			}

		default:
			D_REC(FNC"Not recognized !\n");
			break;
		}
		break;

	case HCI_IP:                 /* Informational parameters */
		switch (ocf) {
		case READ_BUFFER_SIZE:
			D_REC(FNC"READ_BUFFER_SIZE\n");
			if (r_val[0]) {
				D_ERR("READ_BUFFER_SIZE: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			hci_ctrl.hc_buf.acl_len = CHAR2INT16(r_val[2],r_val[1]);
			hci_ctrl.hc_buf.sco_len = (u32) r_val[3];
			hci_ctrl.hc_buf.acl_num = CHAR2INT16(r_val[5],r_val[4]);
			hci_ctrl.hc_buf.sco_num = CHAR2INT16(r_val[7],r_val[6]);
			printk("\nHW module contains...\n");
			printk("%d ACL buffers at %d bytes\n%d SCO buffers at %d bytes\n\n",
			      hci_ctrl.hc_buf.acl_num, hci_ctrl.hc_buf.acl_len,
			      hci_ctrl.hc_buf.sco_num, hci_ctrl.hc_buf.sco_len);
			break;

		case READ_BD_ADDR:
			printk(FNC"READ_BD_ADDR\n");
			release_cmd_timer();

			if (r_val[0]) {
				D_ERR("READ_BD_ADDR: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}

			PRINTPKT("READ_BD_ADDR : ", &r_val[1], 6);
			memcpy(hci_ctrl.local_bd, &r_val[1],6);

			wake_up_interruptible(&hci_wq);

			break;

		default:
			D_REC(FNC"Not recognised !\n");
			break;
		}
		break;

	case HCI_TC:                 /* Test Commands */
		switch (ocf) {

		case READ_LOOPBACK_MODE:
			printk(FNC"READ_LOOPBACK_MODE -- not impl\n");
			break;

		case WRITE_LOOPBACK_MODE:
			printk(FNC"WRITE_LOOPBACK_MODE -- not impl\n");
			break;
			
		case ENABLE_DEVICE_UNDER_TEST_MODE:
			printk(FNC"ENABLE_DEVICE_UNDER_TEST_MODE\n");

			if (r_val[0]) {
				D_ERR("ENABLE_DEVICE_UNDER_TEST_MODE: %s\n",
				      get_err_msg(r_val[0]));
				break;
			}
			printk(FNC"*** Local device now under test\n ***");

			break;

		default:
			D_REC(FNC"Not recognised !\n");
			break;
		}
		break;

	case MANUFACTURER_SPEC: {

		/* FIXME: Have to define which hardware we are using */
		D_REC(FNC"MANUFACTURER_SPEC\n");

		switch (ocf) {

		case 0x04:
			DSYS("Synth on set\n");
			release_cmd_timer();
			wake_up_interruptible(&hci_wq);
			break;

		case 0x07:
			DSYS("Digianswer baudrate set\n");
			release_cmd_timer();
			wake_up_interruptible(&hci_wq);
			break;

		case ERICSSON_SET_UART_BAUD_RATE:
			DSYS("Ericsson baudrate set\n");
#ifndef P9A
			release_cmd_timer();
			wake_up_interruptible(&hci_wq);
#endif
			break;

		case ERICSSON_WRITE_BD_ADDR:
			DSYS("Ericsson baudrate set\n");
			release_cmd_timer();
			wake_up_interruptible(&hci_wq);
			break;

		case ERICSSON_READ_REVISION_INFORMATION:
			release_cmd_timer();
			printk("Ericsson HW revision info: %s\n\n",
			       (r_val + 1));
			wake_up_interruptible(&hci_wq);
			break;

		case ERICSSON_ENTER_TEST_MODE:
			release_cmd_timer();
			if (r_val[0]) {
				D_ERR("ERICSSON_ENTER_TEST_MODE %s\n",
				      get_err_msg(r_val[0]));
			}
			wake_up_interruptible(&hci_wq);
			break;

		case ERICSSON_TEST_CONTROL:
			release_cmd_timer();
			if (r_val[0]) {
				D_ERR("ERICSSON_TEST_CONTROL %s\n",
				      get_err_msg(r_val[0]));
			}
			wake_up_interruptible(&hci_wq);
			break;

		case ERICSSON_TX_TEST:
			release_cmd_timer();
			if (r_val[0]) {
				D_ERR("ERICSSON_TX_TEST %s\n", get_err_msg(r_val[0]));
			}
			wake_up_interruptible(&hci_wq);
			break;

		default:
			D_ERR("Manufacturer specific : Invalid reply (0x%x)\n", ocf);
			break;
		}
	}
	break;

	default:
		D_REC(FNC"Not recognised! \n");
		break;
	}
#undef FNC
}

/*Parses an ACL-data packet and copies it into the hci_in-buffer, data is a 
  pointer to the data in the memory, pb_flag is the packet boundary flag 
  which tells whether it is a beginning or a continuing fragment of an 
  L2CAP-packet */

void 
process_acl_data(hci_in_buffer *in_buf, u32 pb_flag)
{
#define FNC "process_acl_data: "

	D_REC(FNC"in_buf->count:%d, in_buf->l2cap_len:%d\n",
	      in_buf->count, in_buf->l2cap_len);

#if HCI_RCV_TIMER
	/* cancel timer if we have received an understandable packet within
	   timeout time*/
	if (rcv_timers == 1) {    
		printk(FNC"Packet OK, removing hci_receive timer\n");
		rcv_timers--;
		del_timer(&hci_receive_timer);
	}
#endif
	if (pb_flag == L2CAP_FRAME_START) {
		in_buf->l2cap_len = 0;
		l2cap_receive_data(in_buf->buf, in_buf->count,
				   in_buf->hci_hdl, &in_buf->l2cap_len);

	} else if (in_buf->count == in_buf->l2cap_len) {
		l2cap_receive_data(in_buf->buf, in_buf->count,
				   in_buf->hci_hdl, &in_buf->l2cap_len);
	}
#undef FNC
}

/* Process an SCO-data packet, i.e. received audio from the Bluetooth module */

void
process_sco_packet(u8 *data, u32 hci_hdl, u32 len)
{
#define FNC "process_sco_packet: "
	hci_in_buffer *in_buf;
	if (!(in_buf = get_free_inbuffer())) {
		printk(FNC"couldn't find free in buffer\n");
	}
	memcpy(in_buf->buf_ptr, data, len);
	in_buf->hci_hdl = hci_hdl;
	in_buf->buf_ptr += len;
	in_buf->count += len;

	/* FIXME - Finish this function... */
#undef FNC
}

void
set_hci_con(u8 *bd, s32 con_hdl)
{
	s32 i;

	for (i = 0; i < MAX_NBR_OF_CONNECTIONS; i++) {
		if (hci_ctrl.con[i].state == NOT_CONNECTED) {
			memcpy(hci_ctrl.con[i].bd, bd, 6);
			hci_ctrl.con[i].con_hdl = con_hdl;
			hci_ctrl.con[i].state = UNIT_ACTIVE;
			return;
		}
	}
}

void reset_hci_con_bd(u16 con_hdl)
{
	s32 i;

	for (i = 0; i < MAX_NBR_OF_CONNECTIONS; i++) {
		if (hci_ctrl.con[i].con_hdl == con_hdl) {
			hci_ctrl.con[i].state = NOT_CONNECTED;
			hci_ctrl.con[i].con_hdl = -1;
			hci_ctrl.con[i].name[0] = 0;
			return;
		}
	}
}

void
set_hci_con_name(u8 *bd, u8 *name)
{
	s32 i;

	if (!name) {
		D_ERR("set_hci_con_name: No name defined\n");
		return;
	}

	for (i = 0; i < MAX_NBR_OF_CONNECTIONS; i++) {
		if (memcmp(hci_ctrl.con[i].bd, bd, 6) == 0) {
			strcpy(hci_ctrl.con[i].name, name);
			return;
		}
	}
}

s32 hci_sprint_local_bd(u8 *buf)
{
	return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
		       hci_ctrl.local_bd[5], hci_ctrl.local_bd[4],
		       hci_ctrl.local_bd[3], hci_ctrl.local_bd[2],
		       hci_ctrl.local_bd[1], hci_ctrl.local_bd[0]);
}
		       
		       

s32 hci_sprint_remote_info(u8 *buf)
{
	s32 pos = 0;
	s32 i;

	pos += sprintf(buf + pos, "unit_id unit_bd_address uint_mode unit_name\n");


	/* Then we printout the other connections bd addresses and their user
	   friendly device names */
	for (i = 0; i < MAX_NBR_OF_CONNECTIONS; i++) {
		if (hci_ctrl.con[i].state != NOT_CONNECTED) {
			pos += sprintf(buf + pos, "%d ", i);
			pos += sprintf(buf + pos,"%02x:%02x:%02x:%02x:%02x:%02x ",
				       hci_ctrl.con[i].bd[5], hci_ctrl.con[i].bd[4],
				       hci_ctrl.con[i].bd[3], hci_ctrl.con[i].bd[2],
				       hci_ctrl.con[i].bd[1], hci_ctrl.con[i].bd[0]);
			if (hci_ctrl.con[i].state == UNIT_ACTIVE) {
				pos += sprintf(buf + pos, "ACTIVE ");
			} else if (hci_ctrl.con[i].state == UNIT_PASSIVE) {
				pos += sprintf(buf + pos, "PASSIVE ");
			} else {
				pos += sprintf(buf + pos, "UNKNOWN ");
			}
			pos += sprintf(buf + pos, "%s\n", hci_ctrl.con[i].name);
		}

	}
	
	pos += sprintf(buf + pos, "\nFree data buffers in HW : %d\n", 
		       hci_ctrl.hc_buf.acl_num);
	pos += sprintf(buf + pos, "Free command buffers in HW : %d\n", 
		       hci_ctrl.hc_buf.cmd_num);

	return pos;
}

/* Marks the buffer empty, so new hci data, from the same or another
   connection handler can be written in it */

void 
hci_clear_buffer(u32 hci_hdl)
{
#define FNC "hci_clear_buffer: "
	hci_in_buffer *in_buf = NULL;
  
	D_CTRL(FNC"\n");

	if (!(in_buf = get_inbuffer(hci_hdl))) {
		printk(FNC"couldn't find in buffer hci_hdl %d\n", hci_hdl);
		return;
	}

	in_buf->buf_ptr = in_buf->buf;
	in_buf->count = 0;
	in_buf->l2cap_len = 0;
	in_buf->empty = TRUE;
	in_buf->nbr_of_hci_pkt = 0;
#undef FNC
}

/* Finds the in buffer for a given connection handler */
 
hci_in_buffer*
get_inbuffer(u32 hci_hdl)
{
#define FNC "get_inbuffer: "
	u32 i;
  
	i = 0;
	while ((hci_ctrl.hci_in_buf[i].hci_hdl != hci_hdl) && 
	       (i < NBR_OF_HCI_INBUFFERS)) {
		i++;
	}
	if (i == NBR_OF_HCI_INBUFFERS
	    && (hci_ctrl.hci_in_buf[i].hci_hdl != hci_hdl)) {
		D_ERR(FNC"WARNING! No inbuffer with hci_hdl %d\n",
		       hci_hdl);
		return &hci_ctrl.trash_buffer;
	}
	D_CTRL(FNC"Found inbuffer for hci_hdl %d 0x%x\n",hci_hdl, 
	       &hci_ctrl.hci_in_buf[i]);
	return &hci_ctrl.hci_in_buf[i];
#undef FNC
}

/* Finds an free in buffer*/

hci_in_buffer* 
get_free_inbuffer(void)
{
#define FNC "get_free_inbuffer: "
	u32 i;

	i = 0;
	while ((!hci_ctrl.hci_in_buf[i].empty) && 
	       (i < NBR_OF_HCI_INBUFFERS)) {
		i++;
	}

	if (i == NBR_OF_HCI_INBUFFERS && (!hci_ctrl.hci_in_buf[i].empty)) {
		D_ERR(FNC"No freee buffer found, discarding data\n");
		return &hci_ctrl.trash_buffer;
	}

	D_CTRL(FNC"inbuffer %d was free\n",i);
	hci_ctrl.hci_in_buf[i].empty = FALSE;
	return &hci_ctrl.hci_in_buf[i];
#undef FNC
}

void 
hci_init(void)
{
	u32 i;

	DSYS("Initialising HCI\n");

#ifdef HCI_EMULATION
	DSYS("*** HCI emulator on ***\n");
#else
	DSYS("HCI emulator off\n");
#endif
	
	DSYS("Initialising HCI inbuffers [%d]\n", HCI_IN_SIZE);
	/* Initiate the hci inbuffers */
	for (i = 0; i < NBR_OF_HCI_INBUFFERS; i++) {
		hci_ctrl.hci_in_buf[i].buf_ptr = hci_ctrl.hci_in_buf[i].buf;
		hci_ctrl.hci_in_buf[i].count = 0;
		hci_ctrl.hci_in_buf[i].l2cap_len = 0;
		hci_ctrl.hci_in_buf[i].empty = TRUE;
	}

	for (i = 0; i < MAX_NBR_OF_CONNECTIONS; i ++) {
		hci_ctrl.con[i].state = NOT_CONNECTED;
		hci_ctrl.con[i].con_hdl = -1;
	}

/* Here we set the buffer sizes to zero, just to avoid that they should get
   undefined values if the initiating read buffersize fails */

	hci_ctrl.hc_buf.acl_len = 0;
	hci_ctrl.hc_buf.sco_len = 0;
	hci_ctrl.hc_buf.acl_num = 0;
	hci_ctrl.hc_buf.sco_num = 0;

	init_cmd_buf();

	hci_ctrl.nbr_of_connections = 0;
	hci_ctrl.hc_buf.cmd_num = 1;

	if (bt_current_hw != HW_DIGIANSWER_RS232)
	{	
		hci_reset();
	}
	
	/* always... */
	read_buffer_size();
	
	if (bt_current_hw == HW_ERICSSON)
		read_ericsson_rev_info();

	/* FIXME: This should be set in a more dynamicaly way */
	write_class_of_device(0, 3, 10);

	//read_stored_link_key(NULL,1);

	//write_authentication_enable(1);

#ifdef HOST_FLOW_CTRL
	printk("HOST FLOW CONTROL ON\n");
	set_host_controller_flow_control(TRUE);
	
	host_buffer_size(HCI_ACL_LEN,HCI_SCO_LEN,HCI_ACL_NUM,HCI_SCO_NUM);
#endif

#if HCI_RCV_TIMER
/* If we have lost sync we start this timer, resets HCI state machine,
   issue a command and then wait for a chance to sync with new data */
	init_timer(&hci_receive_timer);
	hci_receive_timer.function = hci_rec_data_timeout;
	hci_receive_timer.data = 0;
#endif


#if DIGI_DONGLE_SUPPORT
	set_digi_baudrate(115200);
#endif

}

#if HCI_RCV_TIMER
void 
hci_rec_data_timeout(unsigned long ptr)
{
#define FNC "hci_rec_data_timeout: "
	printk(FNC"resetting state machine in HCI\n");

	rcv_timers--;

	if (state == WAIT_FOR_TIMEOUT)
		state = WAIT_FOR_PACKET_TYPE;
	else
		D_ERR(FNC"Wrong state!\n");
#undef FNC
}
#endif

/* Start of the definition of the functions performing all the different
   HCI commands. The functions will be defined in the same order as they are
   described in the HCI specification, part H:1 of the bluetooh core
   specification. Functions that begins with hci_ can be reached from utside
   this file*/

/* Definition of Link Control Commands */

/* This function will cause the Bluetooth device to enter Inquiry Mode. Inquiry
   Mode is used to discover other nearby Bluetooth devices. The LAP input
   parameter contains the LAP from which the inquiry access code shall be
   derived when the inquiry procedure is made. The Inquiry_length parameter
   specifies the total duration of the Inquiry Mode and, when this time 
   expires, Inquiry will be halted. The Num_Responses parameter specifies 
   the number of responses that can be received before the Inquiry is halted.*/

inquiry_results* 
hci_inquiry(u8 *lap, u8 inq_len, u8 num_resp)
{
	s32 tmp;

	D_CTRL("Sending inquiry()\n");

	/* FIXME: Check if lap is valid first */
	/* Set default lap */
	lap[0] = 0x33;
	lap[1] = 0x8b;
	lap[2] = 0x9e;

	if (!inq_res) {
		kfree(inq_res);
	}

	inq_res = (inquiry_results*) kmalloc(sizeof(inquiry_results)
					     + 6 * num_resp, GFP_ATOMIC);
	inq_res->nbr_of_units = 0;

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = INQUIRY;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, lap, 3);
	c_pkt.data[3] = inq_len;
	c_pkt.data[4] = num_resp;
	c_pkt.len = 5;

	print_data("hci_inquiry", (u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	interruptible_sleep_on(&inq_wq);
	if ( tmp >= 0) {
		return inq_res;
	} else {
		return NULL;
	}
}

/* This function will cause the Link Manager to create a connection to the
   Bluetooth device with the BD_ADDR specified by the function parameters.
   This function causes the local Bluetooth device to begin the Page process
   to create a link level connection. The other parameters are psrm: Pagescan
   repetition mode; psm: Pagescan mode; c_off: Clock offset */

s32 
create_connection(u8 bd_addr[], u32 pkt_type, u8 psrm, u8 psm, u32 c_off,
		  u32 rol_sw)
{ 
	D_CTRL("create_connection\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = CREATE_CONNECTION;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, bd_addr, 6);

	c_pkt.data[6] = (pkt_type & 0xff);
	c_pkt.data[7] = (pkt_type >> 8);
	c_pkt.data[8] = psrm;
	c_pkt.data[9] = psm;
	c_pkt.data[10] = (c_off & 0xff);
	c_pkt.data[11] = (c_off >> 8);
	c_pkt.data[12] = rol_sw;
	c_pkt.len = 13;

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);  
}

/* The Disconnection function is used to terminate an existing connection. The
   Connection_Handle function parameter indicates which connection is to be
   disconnected. The Reason function parameter indicates the reason for ending
   the connection, see page 552 specification core 1.0B */
s32 
disconnect(u32 hdl, u8 reason)
{
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = DISCONNECT;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, &hdl, 2);
	c_pkt.data[2] = reason;
	c_pkt.len = 3;

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

s32
hci_add_sco_connection(u32 hci_hdl)
{
	D_CTRL("Sending hci_add_sco_link\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = ADD_SCO_CONNECTION;
	c_pkt.ogf = HCI_LC;

	c_pkt.data[0] = hci_hdl & 0xff;
	c_pkt.data[1] = hci_hdl >> 8;
	c_pkt.data[2] = 0xe0;
	c_pkt.data[3] = 0x00;
	c_pkt.len = 4;

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

/* A positive response to a previous connection request, role = 0 sets master,
   role = 1 sets slave */
s32 
accept_connection_request(u8 bd[], u8 role)
{
#define FNC "accept_connection_request: "
	D_CTRL(FNC"bd_addr %x %x %x %x %x %x\n",bd[0], bd[1], bd[2],
	       bd[3], bd[4], bd[5]);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = ACCEPT_CONNECTION_REQUEST;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, bd, 6);
	c_pkt.data[6] = role;
	c_pkt.len = 7;  

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
#undef FNC
}

/* The Reject_Connection_Request function is used to decline a new incoming
   connection request. The Reject_Connection_Request function shall only be
   called after a Connection Request event has occurred. */

s32 
reject_connection_request(u8 bd[], u32 reason)
{
	u32 c = 0;

	D_CTRL("reject_connection_request: to bd_addr %x %x %x %x %x %x\n",
	       bd[0],bd[1],bd[2],bd[3],bd[4],bd[5]);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = REJECT_CONNECTION_REQUEST;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, bd, 6);
	c += 6;
	c_pkt.data[c] = reason;
	c += 1;
	c_pkt.len = c;

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

s32
link_key_request_reply(u8 *bd, u8* link_key)
{
#define FNC "link_key_request_reply                        : "
	u32 c = 0;

	print_data(FNC" BD_addr", bd, 6);
	print_data(FNC" Link Key", link_key, 16);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = LINK_KEY_REQUEST_REPLY;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, bd, 6);
	c += 6;
	memcpy(c_pkt.data + c, link_key, 16);
	c += 16;
	c_pkt.len = c;

	return send_cmd((u8*) &c_pkt , c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
#undef FNC
}

s32
pin_code_request_reply(u8 *bd, u8 *pin, u8 pin_len)
{
#define FNC "pin_Code_Request_Reply: "
	u32 c = 0;

	print_data(FNC"New pin is\n", pin, pin_len); 

	print_data(FNC" BD_addr", bd, 6);
	print_data(FNC" pin", pin, pin_len);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = PIN_CODE_REQUEST_REPLY;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, bd, 6);
	c += 6;
	c_pkt.data[c] = pin_len;
	c += 1;
	memcpy(c_pkt.data + c, pin, 16);
	c += 16;

	c_pkt.len = c;
	
	return send_cmd((u8*) &c_pkt , c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
#undef FNC
}


/* The Change_Connection_Packet_Type function is used to change which packet
   types can be used for a connection that is currently established. See page
   564 in the BT specification core v1.0B for more info about the packet types
*/

s32 
change_connection_packet_type(u32 hci_hdl, u32 pkt_type)
{
#define FNC "change_connection_packet_type: "
	D_CTRL(FNC"for connnection handle 0x%x\n", hci_hdl);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = CHANGE_CONNECTION_PACKET_TYPE;
	c_pkt.ogf = HCI_LC;

	c_pkt.data[0] = hci_hdl & 0xff;
	c_pkt.data[1] = (hci_hdl >> 8) & 0xff;
	c_pkt.data[2] = pkt_type & 0xff;
	c_pkt.data[3] = (pkt_type >> 8) & 0xff;
	c_pkt.len = 4;

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
#undef FNC
}

s32
remote_name_request(u8 *bd)
{
#define FNC "remote_name_request: "
	s32 c = 0;
	s32 zero = 0;
		
	printk(FNC" for bd address 0x%02x:%02x:%02x:%02x:%02x:%02x:\n",
	       bd[5], bd[4], bd[3], bd[2], bd[1], bd[0]);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = REMOTE_NAME_REQUEST;
	c_pkt.ogf = HCI_LC;

	memcpy(c_pkt.data, bd, 6);
	c += 6;
	memcpy(c_pkt.data + c, &zero, 4);
	c += 4;

	c_pkt.len = c;

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
#undef FNC
}

/* Definitions of the Link Policy Commands functions */

/* Definitions of the Host Controller & Basband Command functions */

/* Sends reset command to the host controller */
s32
hci_reset()
{
  D_CTRL("hci_reset()\n");
  c_pkt.type = CMD_PKT;
  c_pkt.ocf = RESET;
  c_pkt.ogf = HCI_HC;
  c_pkt.len = 0;
  
  return send_cmd((u8*) &c_pkt , c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

s32 
flush(u32 hdl)
{
	D_CTRL("flush()\n");
  
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = FLUSH;
	c_pkt.ogf = HCI_HC;

	c_pkt.data[0] = (hdl & 0xff);
	c_pkt.data[1] = ((hdl >> 8) & 0xff);
	c_pkt.len = 2;

	return send_cmd((u8*) &c_pkt, 2 + CMD_HDR_LEN + HCI_HDR_LEN);
}
 
s32
read_stored_link_key(u8 *bd, u8 flag)
{
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = READ_STORED_LINK_KEY;
	c_pkt.ogf = HCI_HC;
	memcpy(c_pkt.data, bd , 6);
	c_pkt.data[6] = flag;
	c_pkt.len = 7;
  
	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

s32
hci_change_local_name(u8 *new_name)
{
#define NAME_LEN 248
	s32 tmp;
	
	D_CTRL("Sending hci_change_local_name\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = CHANGE_LOCAL_NAME;
	c_pkt.ogf = HCI_HC;
	
	memcpy(c_pkt.data, new_name, strlen(new_name));
	printk("hci_change_local_name to %s\n", new_name);
	c_pkt.len = NAME_LEN;

	tmp = send_cmd((u8*) &c_pkt , c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	
	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
	
	return tmp;

#undef NAME_LEN
}

s32 
write_scan_enable(u32 enable)
{
	u32 tmp;
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = WRITE_SCAN_ENABLE;
	c_pkt.ogf = HCI_HC;
  
	c_pkt.data[0] = enable;
	c_pkt.len = 1;

	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
  
	return tmp;
}

/*
 * This function will write the value for Page_Scan_Activity configuration
 * parameter. The Page_Scan_Interval configuration parameter defines the
 * amount of time between consecutive page scans.
 */

s32 
write_pagescan_activity(u32 interval, u32 wind)
{
	u32 c = 0;

	D_CTRL("write_pagescan_activity()\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = WRITE_PAGESCAN_ACTIVITY;
	c_pkt.ogf = HCI_HC;
  
	memcpy(c_pkt.data + c, &interval, 2);
	memcpy(c_pkt.data + (c+= 2), &wind, 2);
	c_pkt.len = (c+= 2);

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

/*
 * This function will write the value for Inquiry_Scan_Activity configuration
 * parameter. The Inquiry_Scan_Interval configuration parameter defines the
 *  amount of time between consecutive inquiry scans.
 */

s32 
write_inquiryscan_activity(u32 interval, u32 wind)
{
	u32 c = 0;
	D_CTRL("write_inquiryscan_activity()\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = WRITE_INQUIRYSCAN_ACTIVITY;
	c_pkt.ogf = HCI_HC;
  
	memcpy(c_pkt.data + c, &interval, 2);
	memcpy(c_pkt.data + (c+= 2), &wind, 2);
	c_pkt.len = (c+= 2);

	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

/*
 * This function will write the value for the Scan_Enable parameter. The
 * Scan_Enable parameter controls whether or not the Bluetooth device will 
 * periodically scan for page attempts and/or inquiry requests from other
 * Bluetooth devices.
 */

s32
write_authentication_enable(u32 enable)
{
	s32 tmp;
	
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = WRITE_AUTHENTICATION_ENABLE;
	c_pkt.ogf = HCI_HC;
	c_pkt.data[0] = enable & 0xff;
	c_pkt.len = 1;
  
	print_data("write_authentication_enable:",(u8*)&c_pkt,
		   c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	
	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
	
	return tmp;
}

s32
write_class_of_device(u8 minor, u8 major, u16 service_class)
{
	D_CTRL("write_class_of_device\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = WRITE_CLASS_OF_DEVICE;
	c_pkt.ogf = HCI_HC;
	c_pkt.data[0] = minor;
	c_pkt.data[1] = (service_class << 5) | (major & 0x1f);
	c_pkt.data[2] = service_class >> 5;

	c_pkt.len = 3;
  
	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}


#ifdef HOST_FLOW_CTRL
/* This function is used by the Host to turn flow control on or off in the
   direction from the Host Controller to the Host */

s32 
set_host_controller_flow_control(u32 enable)
{
	s32 tmp;
	D_CTRL("set_host_controller_flow_control: enable:%d\n",enable);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = SET_HOST_CONTROLLER_TO_HOST_FLOW_CONTROL;
	c_pkt.ogf = HCI_HC;
  
	c_pkt.data[0] = enable;
	c_pkt.len = 1;

	tmp = send_cmd((u8*) &c_pkt, c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	
	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
	
	return tmp;
}

/* The host_buffer_size function is used by the Host to notify the Host
   Controller about the maximum size of the data portion of HCI ACL and
   SCO Data Packets sent from the Host Controller to the Host. The Host
   Controller will segment the data to be transmitted from the Host
   Controller to the Host according to these size so that the HCI Data
   Packets will contain data with up to these sizes */

s32 
host_buffer_size(u16 acl_len, u8 sco_len, u16 acl_num, u16 sco_num)
{
#define FNC "host_buffer_size: "
	u32 c = 0;
	s32 tmp;

	D_CTRL(FNC"ALC_LEN:%d, SCO_LEN:%d, ACL_NUM:%d, SCO_NUM:%d\n",
	       acl_len, sco_len, acl_num, sco_num);


	c_pkt.type = CMD_PKT;
	c_pkt.ocf = HOST_BUFFER_SIZE;
	c_pkt.ogf = HCI_HC;
  
	memcpy(c_pkt.data + c, &acl_len, 2);
	c_pkt.data[(c+= 2)] = sco_len;
	memcpy(c_pkt.data + (c+= 1), & acl_num, 2);
	memcpy(c_pkt.data + (c+= 2), & sco_num, 2);
	c_pkt.len = (c+= 2);

	tmp = send_cmd((u8*) &c_pkt, c + CMD_HDR_LEN + HCI_HDR_LEN);
	
	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);

	return tmp;
#undef FNC
}


s32 
host_nbr_completed_packets(u32 hci_hdl, u32 nbr_of_packets)
{
#define FNC "host_nbr_completed_packets: "
	u32 c = 0;

	D_CTRL(FNC"hci_hdl:0x%x, for %d packets\n", hci_hdl,nbr_of_packets);

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = HOST_NBR_OF_COMPLETED_PACKETS ;
	c_pkt.ogf = HCI_HC;
  
	c_pkt.data[0] = 1;  /* We only send one connection handler by time */
	memcpy(c_pkt.data + (c+= 1), &hci_hdl, 2);
	memcpy(c_pkt.data + (c+= 2), &nbr_of_packets, 2);
	c_pkt.len = (c+= 2);

	return bt_write_lower_driver((u8*) &c_pkt, c +CMD_HDR_LEN+HCI_HDR_LEN);
#undef FNC
}

#endif /* HOST_FLOW_CTRL */

s32 
read_link_suppervision_to(u32 hdl)
{
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = READ_LINK_SUPERVISON_TO;
	c_pkt.ogf = HCI_HC;
	c_pkt.data[0] = hdl & 0xff;
	c_pkt.data[1] = (hdl >> 8) & 0xff;
	c_pkt.len = 2;
  
	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

s32 
write_link_suppervision_to(u32 hdl, u32 link_to)
{
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = WRITE_LINK_SUPERVISON_TO;
	c_pkt.ogf = HCI_HC;
	c_pkt.data[0] = hdl & 0xff;
	c_pkt.data[1] = (hdl >> 8) & 0xff;
	c_pkt.data[2] = link_to & 0xff;
	c_pkt.data[3] = (link_to >> 8) & 0xff;
	c_pkt.len = 4;
  
	print_data("write_link_to:",(u8*)&c_pkt,
		   c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	return send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
}

/* Defines of Informational Parameters function */


/* The Read_Buffer_Size function is used to read the maximum size of the data
   portion of HCI ACL and SCO Data Packets sent from the Host to the Host 
   Controller. */

s32 
read_buffer_size(void)
{
	s32 tmp;
	
	DSYS("Reading buffer sizes in the module...\n");
	
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = READ_BUFFER_SIZE;
	c_pkt.ogf = HCI_IP;
	c_pkt.len = 0;

	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	
	return tmp; 
}

/*
 * This command will read the value for the BD_ADDR parameter of the local
 * unit, returns bd in little endian
 */ 

s32 
read_bd_addr(u8 *bd)
{
	s32 tmp;

	D_CTRL("read_bd_addr\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = READ_BD_ADDR;
	c_pkt.ogf = HCI_IP;
	c_pkt.len = 0;
	
	/* Init to all zeroes in case cmd fails...*/

	/* Hazard ! what if bd isn't a bd addr... */
	memset(bd, 0, 6); 

	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);

	memcpy(bd, hci_ctrl.local_bd, 6);
	return tmp;
} 


/* Defines of Testing Commands functions */
s32
hci_enable_dut(void)
{
	s32 tmp;
	
	DSYS("Enabling device under test mode (RESET to release)\n");
	
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = ENABLE_DEVICE_UNDER_TEST_MODE;
	c_pkt.ogf = HCI_TC;
	c_pkt.len = 0;

	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	
	return tmp; 
}

/* Defines of some vendor specific debug commands */

/* Ericsson specific commands */

s32
hci_ericsson_enter_test_mode(u16 hci_hdl)
{
	s32 tmp;

	DSYS("Setting remot module with hci handler 0x%04x in test mode\n", hci_hdl);
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = ERICSSON_ENTER_TEST_MODE;
	c_pkt.ogf = MANUFACTURER_SPEC;
	
	c_pkt.data[0] = hci_hdl & 0xff;
	c_pkt.data[1] = (hci_hdl >> 8) & 0xff;
	c_pkt.len = 2;

	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);

	return tmp;
}

	

s32
hci_ericsson_test_control(u16 hci_hdl, u8 *ctrl_seq)
{
	s32 tmp;

	DSYS("sending hci_ericsson_test_control command\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = ERICSSON_TEST_CONTROL;
	c_pkt.ogf = MANUFACTURER_SPEC;
	
	c_pkt.data[0] = hci_hdl & 0xff;
	c_pkt.data[1] = (hci_hdl >> 8) & 0xff;

	memcpy(c_pkt.data + 2, ctrl_seq, 9);
	c_pkt.len = 11;
	
	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);

	return tmp;
}

s32
hci_ericsson_tx_test(u8 *ctrl_seq)
{
	s32 tmp;

	DSYS("sending hci_ericsson_tx_test command\n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = ERICSSON_TX_TEST;
	c_pkt.ogf = MANUFACTURER_SPEC;
	
	memcpy(c_pkt.data, ctrl_seq, 12);
	c_pkt.len = 12;
	
	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);

	return tmp;
}


#if RS232_SUPPORT

s32 
send_negotiation_pkt(void)
{
	u8 pkt[8];
	D_CTRL("send_negotiation_pkt()\n");
	pkt[0] = NEG_PKT;
	pkt[1] = seq_nbr;
	seq_nbr++;
	pkt[2] = SET_UART(0x0,0x0,0x0,0xb);
	pkt[3] = (SET_BAUD_RATE(115200) & 0xff);
	pkt[4] = ((SET_BAUD_RATE(115200) >> 8) & 0xff);
	pkt[5] = 10; /*?!? Try with this ?!?*/
	pkt[6] = 0;
	pkt[7] = SET_PROTOCOL_MODE(0,0,0,1,0,0);
  
	return send_cmd(pkt,8);
}

#endif

s32 
set_digi_baudrate(u32 baudrate)
{
	u8 pkt[5];
	s32 tmp;
  
	D_CTRL("set_digi_baudrate : %d baud\n", baudrate);
	pkt[0] = CMD_PKT;
	pkt[1] = 0x07;
	pkt[2] = 0xfc;
	pkt[3] = 0x01;
	switch (baudrate) {
	case 9600: pkt[4] = 0x03;
		break;
	case 19200:pkt[4] = 0x05;
		break;
	case 38400: pkt[4] = 0x07;
		break;
	case 57600: pkt[4] = 0x09;
		break;
	case 115200: pkt[4] = 0x09;
		break;
	}
	tmp = send_cmd(pkt,5);
	
	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
  
	return tmp;
}

s32 
set_ericsson_baudrate(u32 baudrate)
{
	s32 tmp;
   
	D_CTRL("set_ericsson_baudrate %d baud\n", baudrate);
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = ERICSSON_SET_UART_BAUD_RATE;
	c_pkt.ogf = 0x3f;
	c_pkt.len = 1;
	switch (baudrate) {

	case 57600: c_pkt.data[0] = 0x03;
		break;
	case 115200: c_pkt.data[0] = 0x02;
		break;
	case 230400: c_pkt.data[0] = 0x01;
		break;
	case 460800: c_pkt.data[0] = 0x00;
		break;
	default:
		D_ERR("set_ericsson_baudrate : Baudrate not supported\n");
		break;
		
	}

#ifndef P9A	
	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);
	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
#else
	if (hci_ctrl.hc_buf.cmd_num < 1) {
		DSYS("set_ericsson_baudrate: sleeping\n");
		interruptible_sleep_on(&set_baudrate_wq);
	}
	tmp = bt_write_lower_driver((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	hci_ctrl.hc_buf.cmd_num -= 1;
				    
#endif
	DSYS("set_ericsson_baudrate: Baudrate set command sent\n");
	return tmp;
	
}

s32 
set_ericsson_bd_addr(u8 bd[6])
{
	u8 pkt[10];
	u8 tmp_bd[6];
	s32 tmp, i;
  
	for (i = 0; i < 6; i++) {
		tmp_bd[5-i] = bd[i];
	}

	PRINTPKT("set_ericsson_bd_addr :", bd, 6);
	pkt[0] = CMD_PKT;
	pkt[1] = 0x0d;
	pkt[2] = 0xfc;
	pkt[3] = 0x06;
	memcpy(pkt + 4, tmp_bd, 6);

	tmp = send_cmd(pkt,10);

	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
  
	return tmp;
}
s32
read_ericsson_rev_info(void)
{
	s32 tmp;
	
	D_CTRL("read_ericsson_rev_info: \n");
	c_pkt.type = CMD_PKT;
	c_pkt.ocf = 0xf;
	c_pkt.ogf = 0x3f;
	c_pkt.len = 0;

	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	start_cmd_timer();
	interruptible_sleep_on(&hci_wq);
  
	return tmp;
}

/* (temp) Used to set ericsson module in test mode */
s32
synth_on(void)
{
	s32 tmp;
	DSYS("synth on\n");

	c_pkt.type = CMD_PKT;
	c_pkt.ocf = 0x04;
	c_pkt.ogf = 0x3f;
	c_pkt.len = 0x04;
	c_pkt.data[0]=0x01;
	c_pkt.data[1]=0x1b;
	c_pkt.data[2]=0x00;
	c_pkt.data[3]=0xc0;

	tmp = send_cmd((u8*) &c_pkt ,c_pkt.len + CMD_HDR_LEN + HCI_HDR_LEN);

	return tmp; 
}

void 
init_cmd_buf(void)
{
	u32 i;
	cmd_t *tmp = NULL;
 
	D_CMD("init_cmd_buf: Initializing\n");
  
	while ((tmp = get_next_cmd()) != NULL) {
		kfree(tmp->data);
	}
  
	cmd_buf.first_free = 0;
	cmd_buf.next_to_send = 0;
	cmd_buf.count = 0;
	for (i = 0; i < NBR_CMD_BUFS; i++) {
		cmd_buf.buf[i].data = NULL;
	}        
}

/* returns next cmd buf to send, NULL if nothing in buf */ 

cmd_t* get_next_cmd(void)
{
	cmd_t *tmp;

	if (cmd_buf.count > 0) {
		/* If buffer contains data, get next_to_send */
		tmp = &(cmd_buf.buf[cmd_buf.next_to_send]);

		cmd_buf.count--;
    
		/* update next_to_send */
		if (cmd_buf.next_to_send >= (NBR_CMD_BUFS - 1)) {
			/* If we are at last cmd in buffer, wrap ! */
			cmd_buf.next_to_send = 0;
			D_CMD("get_next_cmd: next_to_send WRAP\n");
		} else {
			cmd_buf.next_to_send += 1;
		}
	} else {
		/* no cmd:s in buffer */
		tmp = NULL;
	}
	return tmp;
}

s32 
insert_cmd(u8 *cmd, u8 len)
{

	if (cmd_buf.count == NBR_CMD_BUFS) {
		printk("insert_cmd: WARNING Command buffer full !\n");
		return -1;
	}

	/* Allocate memory (freed from send_cmd) */
	cmd_buf.buf[cmd_buf.first_free].data = (u8*)kmalloc(len, GFP_ATOMIC);

	memcpy(cmd_buf.buf[cmd_buf.first_free].data, cmd, len);
	cmd_buf.buf[cmd_buf.first_free].len = len;
	cmd_buf.count++;

	if (cmd_buf.first_free >= (NBR_CMD_BUFS - 1)) {
		cmd_buf.first_free = 0; /* wrap */
		D_CMD("insert_cmd: WRAP\n");
	} else {
		cmd_buf.first_free += 1;
	}
	return 0;
}

/* returns nbr cmd:s left in queue after trying to send all */
s32 
send_cmd_queue(void)
{
	cmd_t *tmp = NULL;
  
	D_CMD("send_cmd_queue: start (cmd_num:%d)\n", 
	      hci_ctrl.hc_buf.cmd_num);

	while ((hci_ctrl.hc_buf.cmd_num > 0)
	       && ((tmp = get_next_cmd()) != NULL)) {
    
		/* more in queue and we can send */
#ifdef HCI_EMULATION
		hci_emulator(tmp->data, tmp->len);
#else  
		bt_write_lower_driver(tmp->data, tmp->len);
#endif
		D_CMD("send_cmd_queue : sent data..., freeing memory\n");
		kfree(tmp->data);
		hci_ctrl.hc_buf.cmd_num -= 1;
	}  
	D_CMD("send_cmd_queue : %d cmds left in queue\n", cmd_buf.count);

	if (hci_ctrl.hc_buf.cmd_num > 0) {
		wake_up_interruptible(&set_baudrate_wq);
	}
	
	return cmd_buf.count;
}

s32 
send_cmd(u8 *cmd, u8 len)
{

	D_CMD("send_cmd : cmd_num %d\n", hci_ctrl.hc_buf.cmd_num);
   
	/* if cmd buffer hold data, try to send those first */

	if ((send_cmd_queue() > 0) || (hci_ctrl.hc_buf.cmd_num <= 0)) {
		/* More cmds in queue and we couldn't send any now, 
		   queue this one aswell */
      
		D_CMD("send_cmd: Queueing HCI command\n");

		insert_cmd(cmd,len);
      
		D_CMD("cmd_num : %d\n", hci_ctrl.hc_buf.cmd_num);

		return 0;
	}
   
	/* no commands queued, send this one now */
   
	if (hci_ctrl.hc_buf.cmd_num > 0) {
#ifdef HCI_EMULATION
		hci_emulator(cmd, len);
#else
		bt_write_lower_driver(cmd, len); /* not kmalloced ... */
#endif
		hci_ctrl.hc_buf.cmd_num -= 1;
		D_CMD("send_cmd: Sucessfully sent HCI command\n");
	}
	/* FIXME: return what bt_write_lower_driver return instead */
	return 0;
}

void 
start_cmd_timer(void)
{
#ifdef __KERNEL__
	init_timer(&hci_cmd_timer);
	hci_cmd_timer.function = hci_cmd_timeout;
	hci_cmd_timer.data = 0;
	hci_cmd_timer.expires = jiffies + 10*HZ;
	add_timer(&hci_cmd_timer);
#endif
}

void 
release_cmd_timer(void)
{
#ifdef __KERNEL__
	D_CMD("release_cmd_timer: releasing timer\n");
	del_timer(&hci_cmd_timer);
#endif
}

#ifdef __KERNEL__
static void 
hci_cmd_timeout(unsigned long ptr)
{  
	hci_ctrl.hc_buf.cmd_num += 1;

	printk("Timeout when waiting for command response\n");
	wake_up_interruptible(&hci_wq);
}
#endif


s32
hci_test_connect_req(u8 *bd)
{
	if (create_connection(bd, 0xcc18, 0x00, 0x00, 0x00, 0x00)) {
		D_ERR("hci_test_connect_req, error sending command\n");
	}
	interruptible_sleep_on(&test_wq);
	return test_hci_hdl;
}


/* Sends a connect request to the BT unit with the address bd_addr */

s32 
lp_connect_req(u8 bd_addr[])
{

	PRINTPKT("lp_connect_req to : bd ", bd_addr, 6);
	/* FIXME, store the inparameters in the ctrl-block instead */
  
	return create_connection(bd_addr, 0xcc18, 0x00, 0x00, 0x00, 0x00);
}

/* A response from L2CAP to a previous lp_connect_ind */

s32 
lp_connect_rsp(u8 bd_addr[], u32 cfm)
{ 
	D_CMD("lp_connect_rsp cfm %d \n", cfm);
	if (cfm) {
		return accept_connection_request(bd_addr, 0x01);
	} else {
		return reject_connection_request(bd_addr, 0x0d);
		/* FIXME: 0x0d = due to limited resourses store this
		   somewhere else */
	}
}

/* Disconnect a certain connection handler */

s32 
lp_disconnect(u32 hci_hdl)
{
	return disconnect(hci_hdl, 0x14);
	/* FIXME: 0x14 = Low Resources store this somewhere else */
}

/* Sends the data in the bt_tx_buf object, in HCI packets */
s32 
hci_send_data(bt_tx_buf *tx_buf)
{
	s32 bytes2send;
 
	tx_buf->pb_flag = L2CAP_FRAME_START;

	D_SND("hci_send_data: %d bytes \n",tx_buf->cur_len);

	bytes2send = tx_buf->cur_len;
	while (bytes2send) {
    
		/* If more buffer space in HW and no data in send buffer that
		   should be sent first, then send this data right away ! */

		if ((hci_ctrl.hc_buf.acl_num > 0) && (buf_count() == 1)) {

			D_SND("hci_send_data: send one packet immediately \n");

			D_QUEUE("<--%d (%d)\n", buf_byte_count(), 
				hci_ctrl.hc_buf.acl_num);      
			
			hci_ctrl.hc_buf.acl_num -= 1;
			bytes2send -= send_acl_packet(tx_buf);
		} else {
          
			D_QUEUE("-->%d (%d)\n", buf_byte_count(), 
				hci_ctrl.hc_buf.acl_num);
			return 0;
		}  
	}
	return 0;
}

u32
send_acl_packet(bt_tx_buf *tx_buf)
{
	u32 c;
	u8* cur_data;

	if (tx_buf->pb_flag == L2CAP_FRAME_CONT) {
		printk("send_acl_packet: There shouldn't be any continuation frames here ?!?\n");
	}
	
	/* Calculate the maximum packet length we can send */
	c = MIN(tx_buf->cur_len, hci_ctrl.hc_buf.acl_len);
	D_SND("send_acl_packet: send %d bytes (excl HCI header) out of %d\n",
	      c, tx_buf->cur_len);
  
	/* Set the temporary cur_data pointer to point at the current data to
	   be sent */
	cur_data = tx_buf->data + (tx_buf->subscr_len - tx_buf->cur_len);
 
	D_SND("send_acl_packet: There are %d bytes space for the headers\n",
	      tx_buf->subscr_len - tx_buf->cur_len);
  
	/* Move the pointer so the HCI header can be filled in, higher
	   layers should have allocated space for this */
	cur_data -= (ACL_HDR_LEN + HCI_HDR_LEN);
	
	if ((cur_data < tx_buf->data) || 
	    (cur_data > (tx_buf->data+tx_buf->subscr_len)))
	 {
	   D_ERR("send_acl_packet : Corrupting btmem buffers !!!\n");
	 }
	
	/* Fills in the HCI header in the packet */
	set_acl_hdr(cur_data, c, tx_buf->pb_flag, tx_buf->bc_flag, 
		    tx_buf->hci_hdl);

	/* Send the whole packet plus its header to the serial driver */
	PRINTPKT("send_acl_packet:", cur_data, c + ACL_HDR_LEN + HCI_HDR_LEN);

#ifdef HCI_EMULATION
	hci_emulator(cur_data, c + ACL_HDR_LEN + HCI_HDR_LEN);
#else
	/* bt_write_lower_driver checks in serial driver if there are room 
	   in serial tx buffer, if 0 is returned there was insufficient 
	   room */

	if (!bt_write_lower_driver(cur_data, 
				   c + ACL_HDR_LEN + HCI_HDR_LEN)) {
		
		/* let the packet stay in bt buffers for later transmission
		   either send all or nothing... 
		   (HW need complete packets) */

		printk("send_acl_packet: cannot send now, leave in buffers\n");
		return 0;
	}
	
#endif

	/* We always set the next part to be a continous part of the packet */
	tx_buf->pb_flag = L2CAP_FRAME_CONT;

	/* Decrease the current length of the packet */
	tx_buf->cur_len -= c;
  
	/* Check whether to unsubscribe the subscribed buffer or not */
	if (tx_buf->cur_len == 0) {
		D_SND("send_acl_packet: unsubscribing tx_buf.\n");
		unsubscribe_bt_buf(tx_buf);
	}
	else if (tx_buf->cur_len < 0)
	{
		printk("send_acl_packet : negative cur_len...\n");
	}
	return c;
}

void 
set_acl_hdr(u8 *data, u32 len, u8 pb, u8 bc, u32 hci_hdl)
{
	u32 tmp_hdr;

	data[0] = ACL_PKT;
	tmp_hdr = MAKE_ACL_HDR(hci_hdl,pb,bc,len);
	memcpy(data + 1, &tmp_hdr, 4);
}

/* Returns the error messages according to the specification page 746 */

u8* 
get_err_msg(u32 err_code)
{
	switch (err_code) {
	case 0x00: return "Success!";
	case 0x01: return "Unknown HCI Command";
	case 0x02: return "No Connection";
	case 0x03: return "Hardware Failure";
	case 0x04: return "Page Timeout";
	case 0x05: return "Authentication Failure";
	case 0x06: return "Key Missing";
	case 0x07: return "Memory Full";
	case 0x08: return "Connection Timeout";
	case 0x09: return "Max Number Of Connections";
	case 0x0A: return "Max Number Of SCO Connections To A Device";
	case 0x0B: return "ACL connection already exists";
	case 0x0C: return "Command Disallowed";
	case 0x0D: return "Host Rejected due to limited resources";
	case 0x0E: return "Host Rejected due to security reasons";
	case 0x0F: return "Host Rejected due to remote device is only a personal device";
	case 0x10: return "Host Timeout";
	case 0x11: return "Unsupported Feature or Parameter Value";
	case 0x12: return "Invalid HCI Command Parameters";
	case 0x13: return "Other End Terminated Connection: User Ended Connection";
	case 0x14: return "Other End Terminated Connection: Low Resources";
	case 0x15: return "Other End Terminated Connection: About to Power Off";
	case 0x16: return "Connection Terminated by Local Host";
	case 0x17: return "Repeated Attempts";
	case 0x18: return "Pairing Not Allowed";
	case 0x19: return "Unknown LMP PDU";
	case 0x1A: return "Unsupported Remote Feature";
	case 0x1B: return "SCO Offset Rejected";
	case 0x1C: return "SCO Interval Rejected";
	case 0x1D: return "SCO Air Mode Rejected";
	case 0x1E: return "Invalid LMP Parameters";
	case 0x1F: return "Unspecified Error";
	case 0x20: return "Unsupported LMP Parameter Value";
	case 0x21: return "Role Change Not Allowed";
	case 0x22: return "LMP Response Timeout";
	case 0x23: return "LMP Error Transaction Collision";
	case 0x24: return "LMP PDU Not Allowed";
	}
	return "No debug msg defined";
  
}

/********************* HCI EMULATOR STUFF **********************************/
#ifdef HCI_EMULATION

void 
hci_emulator(u8 *data, u32 len)
{	
	if (*data == SCO_PKT) {
		bt_write_lower_driver(data,len);
		hci_ctrl.hc_buf.sco_num += 1;
	} else if (*data == ACL_PKT) {
		/* forward everything to other side */
		bt_write_lower_driver(data,len);
		hci_ctrl.hc_buf.acl_num += 1;
	} else if (*data == CMD_PKT) {
		/* convert command to event */
		command_handler(data);
		hci_ctrl.hc_buf.cmd_num += 1;
	}
}

void 
command_handler(u8 *data)
{
#define FNC "command_handler: "
	u8 o_event[256];
	u32 o_len;

	cmd_pkt *cmd;
	event_struct *out_event;
	
	PRINTPKT(FNC,data,data[3] + CMD_HDR_LEN);

	cmd = (cmd_pkt*) data; /* Skip the packet type byte */
	out_event = (event_struct*) o_event;
	
	switch (cmd->ogf) {

	case HCI_LC:                 /* Host Controller commands */
		switch (cmd->ocf) {
     
		case CREATE_CONNECTION:
			/* Send connection request event */
			D_CMD(FNC"CREATE_CONNECTION->connection request\n");
			o_len = set_con_req_event(out_event, cmd->data, 1);
			bt_write_lower_driver(o_event, o_len);
      
			break;
      
		case DISCONNECT:
			/* Send disconnection complete to both sides, 0x16 is
			   the conde for "Connection Terminated by Local Host"
			   and 0x13 is the code for "Other End Terminated
			   Connection: User Ended Connection" */
			D_CMD(FNC"DISCONNECT->disconnection complete\n");
			o_len = set_discon_cpl_event(out_event, CHAR2INT12(cmd->data[1], cmd->data[0]), 0x13);
			bt_write_lower_driver(o_event, o_len);
			lp_disconnect_ind(CHAR2INT12(cmd->data[1],cmd->data[0]));
			break;
      
		case ACCEPT_CONNECTION_REQUEST:
			D_CMD(FNC"ACCEPT_CONNECTION->connection complete\n");
			o_len = set_con_cpl_event(out_event, 0, cmd->data,
						  HCI_HDL, ACL_CON);
			bt_write_lower_driver(o_event, o_len);
      
			lp_connect_cfm(data + 4, 0, 0);
			break;
      
		case REJECT_CONNECTION_REQUEST:
			D_CMD(FNC"REJECT_CONNECTION->connection complete\n");
			o_len = set_con_cpl_event(out_event, cmd->data[6],
						  cmd->data, HCI_HDL, ACL_CON);
			bt_write_lower_driver(o_event, o_len);
			lp_connect_cfm(data + 4, 0xd, 0);
			break;
      
		default:
			printk(FNC"Unknown command ogf:0x%x,ocf:0x%x\n",
			      cmd->ogf, cmd->ocf);
			break;
		}
		break;

	case HCI_IP:                 /* Informational parameters */
		switch (cmd->ocf) {
		case READ_BUFFER_SIZE:
			D_CMD(FNC"READ_BUFFER_SIZE->setting buffer size\n");
			hci_ctrl.hc_buf.acl_num = ACL_NUM;
			hci_ctrl.hc_buf.acl_len = ACL_LEN;
			hci_ctrl.hc_buf.sco_num = SCO_NUM;
			hci_ctrl.hc_buf.sco_len = SCO_LEN;
			break;
  
		default:
			printk(FNC"Unknown command ogf:0x%x,ocf:0x%x\n",
			      cmd->ogf, cmd->ocf);
			break;
		}
		break;

	default:
		printk(FNC"Unknown command ogf:0x%x,ocf:0x%x\n",
		      cmd->ogf, cmd->ocf);
		break;
	}
#undef FNC
}

s32 
set_cmd_status_event(event_struct *event, u8 *cmd)
{
	event->pkt_type = EVENT_PKT;
	event->event_type = 0x0f;
	event->len = 4;
	event->data[0] = 0x00;
	event->data[1] = 0x01;
	event->data[2] = cmd[0];
	event->data[3] = cmd[1];
	return event->len + EVENT_HDR_LEN + HCI_HDR_LEN;
}

s32 
set_con_req_event(event_struct *event, u8 *bd, u8 con_type)
{
	event->pkt_type = EVENT_PKT;
	event->event_type = 0x04;
	event->len = 10;
	event->data[0] = bd[0]; 
	event->data[1] = bd[1];
	event->data[2] = bd[2];
	event->data[3] = bd[3];
	event->data[4] = bd[4];
	event->data[5] = bd[5];
	event->data[6] = 0;
	event->data[7] = 0;
	event->data[8] = 0;
	event->data[9] = con_type;
	return event->len + EVENT_HDR_LEN + HCI_HDR_LEN;
}

s32 
set_con_cpl_event(event_struct *event, u8 status, u8 *bd, u16 hci_hdl,
                  u8 con_type)
{
	event->pkt_type = EVENT_PKT;
	event->event_type = 0x03;
	event->len = 11;
	event->data[0] = status;
	event->data[1] = hci_hdl & 0xff;
	event->data[2] = hci_hdl >> 8;
	event->data[3] = bd[0];
	event->data[4] = bd[1];
	event->data[5] = bd[2];
	event->data[6] = bd[3];
	event->data[7] = bd[4];
	event->data[8] = bd[5];
	event->data[9] = con_type;
	event->data[10] = 0x00; /* Encryption disabled */
	return event->len + EVENT_HDR_LEN + HCI_HDR_LEN;
}

s32 
set_discon_cpl_event(event_struct *event, u16 hci_hdl, u8 reason)
{
	event->pkt_type = EVENT_PKT;
	event->event_type = 0x05;
	event->len = 4;
	event->data[0] = 0x00; /* Connection successfully disconnected */
	event->data[1] = hci_hdl & 0xff;
	event->data[2] = hci_hdl >> 8;
	event->data[3] = reason;
	return event->len + EVENT_HDR_LEN + HCI_HDR_LEN;
}

#endif /* HCI_EMULATION */

#if 0
/* Used for testing */
void 
show_cmd_buf(void)
{
	s32 i;
	printk("------------------------------------------\n");
	printk("show_cmd_buf: count %d first_free : %d next_to_send : %d\n",
	      cmd_buf.count, cmd_buf.first_free, cmd_buf.next_to_send);

  
	for (i = 0; i < NBR_CMD_BUFS; i++) {
		printk("%d\n", i);
		if (cmd_buf.buf[i].len > 0)
			PRINTPKT("cmd_buf : ", cmd_buf.buf[i].data, cmd_buf.buf[i].len);      
	}  
	printk("------------------------------------------\n");
}
#endif
/****************** END OF FILE hci.c ***************************************/
