/*
*
* Driver for the 3Com Bluetooth PCMCIA card
*
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
* Jose Orlando Pereira <jop@di.uminho.pt>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/bitops.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
/* ======================== Module parameters ======================== */
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>");
MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
MODULE_LICENSE("GPL");
/* ======================== Local structures ======================== */
typedef struct bt3c_info_t {
dev_link_t link;
dev_node_t node;
struct hci_dev *hdev;
spinlock_t lock; /* For serializing operations */
struct sk_buff_head txq;
unsigned long tx_state;
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
} bt3c_info_t;
static void bt3c_config(dev_link_t *link);
static void bt3c_release(dev_link_t *link);
static int bt3c_event(event_t event, int priority, event_callback_args_t *args);
static dev_info_t dev_info = "bt3c_cs";
static dev_link_t *bt3c_attach(void);
static void bt3c_detach(dev_link_t *);
static dev_link_t *dev_list = NULL;
/* Transmit states */
#define XMIT_SENDING 1
#define XMIT_WAKEUP 2
#define XMIT_WAITING 8
/* Receiver states */
#define RECV_WAIT_PACKET_TYPE 0
#define RECV_WAIT_EVENT_HEADER 1
#define RECV_WAIT_ACL_HEADER 2
#define RECV_WAIT_SCO_HEADER 3
#define RECV_WAIT_DATA 4
/* ======================== Special I/O functions ======================== */
#define DATA_L 0
#define DATA_H 1
#define ADDR_L 2
#define ADDR_H 3
#define CONTROL 4
static inline void bt3c_address(unsigned int iobase, unsigned short addr)
{
outb(addr & 0xff, iobase + ADDR_L);
outb((addr >> 8) & 0xff, iobase + ADDR_H);
}
static inline void bt3c_put(unsigned int iobase, unsigned short value)
{
outb(value & 0xff, iobase + DATA_L);
outb((value >> 8) & 0xff, iobase + DATA_H);
}
static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
{
bt3c_address(iobase, addr);
bt3c_put(iobase, value);
}
static inline unsigned short bt3c_get(unsigned int iobase)
{
unsigned short value = inb(iobase + DATA_L);
value |= inb(iobase + DATA_H) << 8;
return value;
}
static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
{
bt3c_address(iobase, addr);
return bt3c_get(iobase);
}
/* ======================== Interrupt handling ======================== */
static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
{
int actual = 0;
bt3c_address(iobase, 0x7080);
/* Fill FIFO with current frame */
while (actual < len) {
/* Transmit next byte */
bt3c_put(iobase, buf[actual]);
actual++;
}
bt3c_io_write(iobase, 0x7005, actual);
return actual;
}
static void bt3c_write_wakeup(bt3c_info_t *info)
{
if (!info) {
BT_ERR("Unknown device");
return;
}
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
return;
do {
register unsigned int iobase = info->link.io.BasePort1;
register struct sk_buff *skb;
register int len;
if (!(info->link.state & DEV_PRESENT))
break;
if (!(skb = skb_dequeue(&(info->txq)))) {
clear_bit(XMIT_SENDING, &(info->tx_state));
break;
}
/* Send frame */
len = bt3c_write(iobase, 256, skb-