diff -Nur linux-orig/drivers/bluetooth/bt950_cs.c linux/drivers/bluetooth/bt950_cs.c --- linux-orig/drivers/bluetooth/bt950_cs.c 1970-01-01 03:00:00.000000000 +0300 +++ linux/drivers/bluetooth/bt950_cs.c 2004-02-04 09:55:04.000000000 +0300 @@ -0,0 +1,1133 @@ +/* + * + * Driver for Bluetooth cards with OXCF950 UART interface + * + * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> + * Albert Rybalkin <albertr@iral.com> + * + * + * 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> + +#ifdef CONFIG_MODVERSIONS +#ifndef MODVERSIONS +#define MODVERSIONS +#endif +#include <linux/modversions.h> +#endif + +#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/timer.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/spinlock.h> +#include <linux/delay.h> + +#include <linux/skbuff.h> +#include <linux/string.h> +#include <linux/serial.h> +#include <linux/serial_reg.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#include <pcmcia/version.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> + +/* Default baud rate: 57600, 115200, 230400 or 460800 */ +#define DEFAULT_BAUD_RATE 460800 + + +/* ======================== Module parameters ======================== */ + + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0x86bc; +static int irq_list[4] = { -1 }; +static long baud_rate = DEFAULT_BAUD_RATE; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(baud_rate, "l"); + +MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Albert Rybalkin <albertr@iral.com>"); +MODULE_DESCRIPTION("BlueZ driver for Bluetooth cards with OXCF950 UART interface"); +MODULE_LICENSE("GPL"); + + + +/* ======================== Local structures ======================== */ + + +typedef struct bt950_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; +} bt950_info_t; + + +static void bt950_config(dev_link_t *link); +static void bt950_release(u_long arg); +static int bt950_event(event_t event, int priority, event_callback_args_t *args); + +static dev_info_t dev_info = "bt950_cs"; + +static dev_link_t *bt950_attach(void); +static void bt950_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 packet types */ +#define PKT_BAUD_RATE_57600 0x80 +#define PKT_BAUD_RATE_115200 0x81 +#define PKT_BAUD_RATE_230400 0x82 +#define PKT_BAUD_RATE_460800 0x83 + +/* 950-specific stuff */ +#define MAX_WAIT 0xFFFF +#define FIFO_SIZE 128 + +#define TR_TX_INT 0x10 /* TTL: TX interrupt trigger level (0-127) */ +#define TR_RX_INT 0x40 /* RTL: RX interrupt trigger level (1-127) */ +#define TR_CTL_LO 0x08 /* FCL: auto flow control LOWER trigger level (0-127) */ +#define TR_CTL_HI 0x60 /* FCH: auto flow control HIGH trigger level (1-127) */ + +/* 950-specific registers and values we use. It should + * eventually go to include/linux/serial_reg.h */ +#define UART_IER_CTS 0x80 /* enable CTS interrupt */ +#define UART_IER_RTS 0x40 /* enable RTS interrupt */ +#define UART_IER_SLP 0x10 /* enable sleep mode */ +#define UART_LCR_650 0xBF /* enable 650-compatible registers access */ +#define UART_LSR_DE 0x80 /* data error */ +#define UART_LSR_ERR (UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI|UART_LSR_DE) +#define UART_IIR_RXTOUT 0x0C /* RX timeout interrupt */ +#define UART_IIR_CTSRTS 0x20 /* CTS or RTS change interrupt */ +#define UART_IIR_RTS 0x40 +#define UART_IIR_CTS 0x80 +#define UART_IIR_MASK 0x3E /* interrupt mask */ +#define UART_SRT 0x0D /* soft reset register */ + + + +/* ======================== Interrupt handling ======================== */ + + +static int bt950_write(unsigned int iobase, int fifo_size, const unsigned char *buf, int len) +{ + int i, actual = 0; + + /* Activate DTR and RTS */ + outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, iobase + UART_MCR); + + /* Wait for CTS flow control */ + for (i = MAX_WAIT; i; i--) + if (inb(iobase + UART_MSR) & UART_MSR_CTS) + break; + + if (!i) { + printk(KERN_WARNING "bt950_cs: Timeout waiting for CTS on write.\n"); + return 0; + } + + /* The TX FIFO should be empty */ + for (i = MAX_WAIT; i; i--) + if (inb(iobase + UART_LSR) & UART_LSR_THRE) + break; + + if (!i) { + printk(KERN_WARNING "bt950_cs: Timeout waiting for empty TX FIFO on write.\n"); + return 0; + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual], iobase + UART_TX); + actual++; + } + + return actual; +} + + +static void bt950_write_wakeup(bt950_info_t *info) +{ + unsigned char lcr; + unsigned int divisor; + + if (!info) { + printk(KERN_WARNING "bt950_cs: Call of write_wakeup for unknown device.\n"); + return; + } + + if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { + set_bit(XMIT_WAKEUP, &(info->tx_state)); + return; + } + + do { + register unsigned int iobase = info->link.io.BasePort1; + register struct sk_buff *skb; + register int len; + + clear_bit(XMIT_WAKEUP, &(info->tx_state)); + + if (!(info->link.state & DEV_PRESENT)) + return; + + if (!(skb = skb_dequeue(&(info->txq)))) + break; + + if (skb->pkt_type & 0x80) { + /* Disable RTS */ + outb((inb(iobase + UART_MCR) & ~UART_MCR_RTS), iobase + UART_MCR); + } + + /* Send frame */ + len = bt950_write(iobase, FIFO_SIZE, skb->data, skb->len); + + set_bit(XMIT_WAKEUP, &(info->tx_state)); + + if (skb->pkt_type & 0x80) { + + wait_queue_head_t wait; + + switch (skb->pkt_type) { + + case PKT_BAUD_RATE_460800: + divisor = 1; + break; + + case PKT_BAUD_RATE_230400: + divisor = 2; + break; + + case PKT_BAUD_RATE_115200: + divisor = 4; + break; + + case PKT_BAUD_RATE_57600: + /* Fall through... */ + + default: + divisor = 8; + break; + } + + /* Wait until the command reaches the baseband */ + init_waitqueue_head(&wait); + interruptible_sleep_on_timeout(&wait, HZ / 10); + + /* Set baud on baseband */ + /* Enable divisor latch access */ + lcr = inb(iobase + UART_LCR) & 0x3F; + outb(lcr | UART_LCR_DLAB, iobase + UART_LCR); + + /* Setup divisor latch */ + outb(divisor & 0x00FF, iobase + UART_DLL); /* divisor latch LOW byte */ + outb((divisor & 0xFF00) >> 8, iobase + UART_DLM); /* divisor latch HI byte */ + + /* Disable divisor latch access */ + outb(lcr, iobase + UART_LCR); + + /* Enable RTS */ + outb((inb(iobase + UART_MCR) | UART_MCR_RTS), iobase + UART_MCR); + + /* Wait before the next HCI packet can be send */ + interruptible_sleep_on_timeout(&wait, HZ); + + } + + if (len == skb->len) { + kfree_skb(skb); + } else { + skb_pull(skb, len); + skb_queue_head(&(info->txq), skb); + } + + info->hdev.stat.byte_tx += len; + + } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); + + clear_bit(XMIT_SENDING, &(info->tx_state)); +} + + +static inline void bt950_receive(bt950_info_t *info) +{ + unsigned int iobase; + int boguscount = 0; + + if (!info) { + printk(KERN_ERR "bt950_cs: Call of receive for unknown device.\n"); + return; + } + + iobase = info->link.io.BasePort1; + + /* Fixme: BUG? */ + inb(iobase + UART_MCR); + + do { + info->hdev.stat.byte_rx++; + + /* Allocate packet */ + if (info->rx_skb == NULL) { + info->rx_state = RECV_WAIT_PACKET_TYPE; + info->rx_count = 0; + if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + printk(KERN_ERR "bt950_cs: Can't allocate mem for new packet.\n"); + return; + } + } + + if (info->rx_state == RECV_WAIT_PACKET_TYPE) { + + info->rx_skb->dev = (void *)&(info->hdev); + info->rx_skb->pkt_type = inb(iobase + UART_RX); + + switch (info->rx_skb->pkt_type) { + + case HCI_EVENT_PKT: + info->rx_state = RECV_WAIT_EVENT_HEADER; + info->rx_count = HCI_EVENT_HDR_SIZE; + break; + + case HCI_ACLDATA_PKT: + info->rx_state = RECV_WAIT_ACL_HEADER; + info->rx_count = HCI_ACL_HDR_SIZE; + break; + + case HCI_SCODATA_PKT: + info->rx_state = RECV_WAIT_SCO_HEADER; + info->rx_count = HCI_SCO_HDR_SIZE; + break; + + default: + /* Unknown packet */ + printk(KERN_WARNING "bt950_cs: Unknown HCI packet with type 0x%02X received.\n", info->rx_skb->pkt_type); + info->hdev.stat.err_rx++; + + kfree_skb(info->rx_skb); + info->rx_skb = NULL; + break; + + } + + } else { + + *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); + info->rx_count--; + + if (info->rx_count == 0) { + + int dlen; + hci_event_hdr *eh; + hci_acl_hdr *ah; + hci_sco_hdr *sh; + + + switch (info->rx_state) { + + case RECV_WAIT_EVENT_HEADER: + eh = (hci_event_hdr *)(info->rx_skb->data); + info->rx_state = RECV_WAIT_DATA; + info->rx_count = eh->plen; + break; + + case RECV_WAIT_ACL_HEADER: + ah = (hci_acl_hdr *)(info->rx_skb->data); + dlen = __le16_to_cpu(ah->dlen); + info->rx_state = RECV_WAIT_DATA; + info->rx_count = dlen; + break; + + case RECV_WAIT_SCO_HEADER: + sh = (hci_sco_hdr *)(info->rx_skb->data); + info->rx_state = RECV_WAIT_DATA; + info->rx_count = sh->dlen; + break; + + case RECV_WAIT_DATA: + hci_recv_frame(info->rx_skb); + info->rx_skb = NULL; + break; + + } + + } + + } + + /* Make sure we don't stay here too long */ + if (boguscount++ > 16) + break; + + } while (inb(iobase + UART_LSR) & UART_LSR_DR); +} + + +static void bt950_interrupt(int irq, void *dev_inst, struct pt_regs *regs) +{ + bt950_info_t *info = dev_inst; + unsigned int iobase; + int boguscount = 0; + int iir, lsr; + + if (!info) { + printk(KERN_ERR "bt950_cs: Call of irq %d for unknown device.\n", irq); + return; + } + + iobase = info->link.io.BasePort1; + + spin_lock(&(info->lock)); + + iir = inb(iobase + UART_IIR); + + while (!(iir & UART_IIR_NO_INT)) { + + switch (iir & UART_IIR_ID) { + case UART_IIR_RLSI: + /* Clear RLSI interrupt */ + lsr = inb(iobase + UART_LSR); + printk(KERN_NOTICE "bt950_cs: RLSI interrupt, LSR=%#x\n", lsr); + /* Fixme: we need to process errors ... */ + break; + case UART_IIR_RDI: + /* Receive interrupt */ + bt950_receive(info); + break; + case UART_IIR_THRI: + /* Transmitter ready for data */ + bt950_write_wakeup(info); + break; + default: + printk(KERN_NOTICE "bt950_cs: Unhandled IIR=%#x\n", iir); + break; + } + + /* Make sure we don't stay here too long */ + if (boguscount++ > 100) + break; + + iir = inb(iobase + UART_IIR); + + } + + spin_unlock(&(info->lock)); +} + +/* ======================== Device specific HCI commands ======================== */ + + +static int bt950_hci_set_baud_rate(struct hci_dev *hdev, int baud) +{ + bt950_info_t *info = (bt950_info_t *)(hdev->driver_data); + struct sk_buff *skb; + + /* Ericsson baud rate command */ + unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 }; + + if (!(skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + printk(KERN_WARNING "bt950_cs: Can't allocate mem for new packet.\n"); + return -1; + } + + switch (baud) { + case 460800: + cmd[4] = 0x00; + skb->pkt_type = PKT_BAUD_RATE_460800; + break; + case 230400: + cmd[4] = 0x01; + skb->pkt_type = PKT_BAUD_RATE_230400; + break; + case 115200: + cmd[4] = 0x02; + skb->pkt_type = PKT_BAUD_RATE_115200; + break; + case 57600: + /* Fall through... */ + default: + baud = 57600; + cmd[4] = 0x03; + skb->pkt_type = PKT_BAUD_RATE_57600; + break; + } + + memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd)); + + skb_queue_tail(&(info->txq), skb); + + printk(KERN_WARNING "bt950_cs: setting baud rate: %d.\n", baud); + + bt950_write_wakeup(info); + + return 0; +} + + +/* ======================== HCI interface ======================== */ + + +static int bt950_hci_flush(struct hci_dev *hdev) +{ + bt950_info_t *info = (bt950_info_t *)(hdev->driver_data); + + /* Drop TX queue */ + skb_queue_purge(&(info->txq)); + + return 0; +} + + +static int bt950_hci_open(struct hci_dev *hdev) +{ + bt950_hci_set_baud_rate(hdev, baud_rate); + set_bit(HCI_RUNNING, &(hdev->flags)); + + return 0; +} + + +static int bt950_hci_close(struct hci_dev *hdev) +{ + if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) + return 0; + + bt950_hci_flush(hdev); + + return 0; +} + + +static int bt950_hci_send_frame(struct sk_buff *skb) +{ + bt950_info_t *info; + struct hci_dev *hdev = (struct hci_dev *)(skb->dev); + + if (!hdev) { + printk(KERN_ERR "bt950_cs: Frame for unknown HCI device (hdev=NULL)."); + return -ENODEV; + } + + info = (bt950_info_t *)(hdev->driver_data); + + switch (skb->pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + }; + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); + skb_queue_tail(&(info->txq), skb); + + bt950_write_wakeup(info); + + return 0; +} + + +static void bt950_hci_destruct(struct hci_dev *hdev) +{ +} + + +static int bt950_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + + + +/* ======================== Card services HCI interaction ======================== */ + + +static int bt950_setup_uart(bt950_info_t *info) +{ + unsigned long flags; + unsigned int iobase = info->link.io.BasePort1; + unsigned char lcr, ier = UART_IER_RDI | UART_IER_RLSI | UART_IER_SLP; + unsigned int divisor = 8; /* Fixme: divisor == 0x0c ??? */ + unsigned char id1, id2, id3, rev; + register int i; + + spin_lock_irqsave(&(info->lock), flags); + + /* Disable interrupts */ + outb(0, iobase + UART_IER); + + /* Activate RTS and OUT2 */ + /* Fixme: is OUT2 used to enable interrupts? */ + outb(UART_MCR_RTS | UART_MCR_OUT2, iobase + UART_MCR); + + /* Setup the FIFO's */ + outb(0, iobase + UART_FCR); + inb(iobase + UART_RX); + outb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_14, iobase + UART_FCR); + + /* Disable divisor latch access */ + lcr = inb(iobase + UART_LCR) & 0x3F; /* mask out UART_LCR_DLAB and UART_LCR_SBC */ + outb(lcr, iobase + UART_LCR); + + /* Read up to 4 bytes from RX FIFO */ + for (i = 0; i < 4; i++) { + inb(iobase + UART_RX); + if (!(inb(iobase + UART_LSR) & UART_LSR_DR)) + break; + } + + /* Wait if CTS/DSR/DCD changing */ + for (i = 1; i < 0x3E8; i++) { + if (!(inb(iobase + UART_MSR) & UART_MSR_ANY_DELTA)) + break; + } + + /* Enable divisor latch access */ + outb(lcr | UART_LCR_DLAB, iobase + UART_LCR); + + /* Setup divisor latch */ + outb(divisor & 0x00FF, iobase + UART_DLL); /* divisor latch LOW byte */ + outb((divisor & 0xFF00) >> 8, iobase + UART_DLM); /* divisor latch HIGH byte */ + + /* Disable divisor latch access */ + outb(lcr, iobase + UART_LCR); + + /* Setup interrupts, enable sleep mode */ + outb(ier, iobase + UART_IER); /* we don't want to handle TX interrupts */ + + /* Skip pending interrupts */ + for (i = 0; i < 4; i++) { + if (inb(iobase + UART_IIR) & UART_IIR_NO_INT) + break; + } + + /* 8N1 */ + lcr = UART_LCR_WLEN8; + outb(lcr, iobase + UART_LCR); + + /* Setup CTS/RTS flow control and 950 enhanced mode */ + outb(UART_LCR_650, iobase + UART_LCR); + outb(UART_EFR_CTS | UART_EFR_RTS | UART_EFR_ECB, + iobase + UART_EFR); + outb(lcr, iobase + UART_LCR); + + /* Read core id and revision */ + outb(UART_ACR, iobase + UART_EMSR); + outb(UART_ACR_ICRRD, iobase + UART_LSR); /* enable ICR read access, we don't need to save the old value of ACR */ + + outb(UART_ID1, iobase + UART_EMSR); + id1 = inb(iobase + UART_LSR); + + outb(UART_ID2, iobase + UART_EMSR); + id2 = inb(iobase + UART_LSR); + + outb(UART_ID3, iobase + UART_EMSR); + id3 = inb(iobase + UART_LSR); + + outb(UART_REV, iobase + UART_EMSR); + rev = inb(iobase + UART_LSR); + + if (id1 != 0x16 || id2 != 0xC9 || id3 != 0x50) { + printk(KERN_ERR "bt950_cs: Unknown UART core %02X%02X%02X found.\n", id1, id2, id3); + spin_unlock_irqrestore(&(info->lock), flags); + return -ENODEV; + } + + + /* Init ICR registers */ + outb(UART_TTL, iobase + UART_EMSR); + outb(TR_TX_INT, iobase + UART_LSR); /* TX interrupt trigger level (0-127) */ + + outb(UART_RTL, iobase + UART_EMSR); + outb(TR_RX_INT, iobase + UART_LSR); /* RX interrupt trigger level (1-127) */ + + outb(UART_FCL, iobase + UART_EMSR); + outb(TR_CTL_LO, iobase + UART_LSR); /* auto flow control LOWER trigger level (0-127) */ + + outb(UART_FCH, iobase + UART_EMSR); + outb(TR_CTL_HI, iobase + UART_LSR); /* auto flow control HIGH trigger level (1-127) */ + + outb(UART_ACR, iobase + UART_EMSR); + outb(UART_ACR_TLENB, iobase + UART_LSR); /* disable ICR read access, enable trigger levels */ + + spin_unlock_irqrestore(&(info->lock), flags); + + return 0; +} + + +static void bt950_stop_uart(bt950_info_t *info) +{ + unsigned long flags; + unsigned int iobase = info->link.io.BasePort1; + + spin_lock_irqsave(&(info->lock), flags); + + /* Disable interrupts */ + outb(0, iobase + UART_IER); + + /* Set RTS and OUT2 low */ + outb(0, iobase + UART_MCR); + + spin_unlock_irqrestore(&(info->lock), flags); +} + + +static int bt950_open(bt950_info_t *info) +{ + struct hci_dev *hdev; + int err; + + spin_lock_init(&(info->lock)); + + skb_queue_head_init(&(info->txq)); + + info->rx_state = RECV_WAIT_PACKET_TYPE; + info->rx_count = 0; + info->rx_skb = NULL; + + /* Setup hardware */ + if ((err = bt950_setup_uart(info)) < 0) + return err; + + /* Timeout before it is safe to send the first HCI packet */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + + + /* Initialize and register HCI device */ + + hdev = &(info->hdev); + + hdev->type = HCI_PCCARD; + hdev->driver_data = info; + + hdev->open = bt950_hci_open; + hdev->close = bt950_hci_close; + hdev->flush = bt950_hci_flush; + hdev->send = bt950_hci_send_frame; + hdev->destruct = bt950_hci_destruct; + hdev->ioctl = bt950_hci_ioctl; + + if (hci_register_dev(hdev) < 0) { + printk(KERN_ERR "bt950_cs: Can't register HCI device %s.\n", hdev->name); + return -ENODEV; + } + + return 0; +} + + +static int bt950_close(bt950_info_t *info) +{ + struct hci_dev *hdev = &(info->hdev); + + bt950_hci_close(hdev); + + /* Stop hardware */ + bt950_stop_uart(info); + + if (hci_unregister_dev(hdev) < 0) + printk(KERN_ERR "bt950_cs: Can't unregister HCI device %s.\n", hdev->name); + + return 0; +} + + + +/* ======================== Card services ======================== */ + + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + + CardServices(ReportError, handle, &err); +} + + +static dev_link_t *bt950_attach(void) +{ + bt950_info_t *info; + client_reg_t client_reg; + dev_link_t *link; + int i, ret; + + /* Create new info device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + memset(info, 0, sizeof(*info)); + + link = &info->link; + link->priv = info; + + link->release.function = &bt950_release; + link->release.data = (u_long)link; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts1 = 8; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + + link->irq.Handler = bt950_interrupt; + link->irq.Instance = info; + + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.Present = + PRESENT_OPTION | PRESENT_STATUS | PRESENT_PIN_REPLACE | + PRESENT_COPY; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &bt950_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + bt950_detach(link); + return NULL; + } + + return link; +} + + +static void bt950_detach(dev_link_t *link) +{ + bt950_info_t *info = link->priv; + dev_link_t **linkp; + int ret; + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + + if (*linkp == NULL) + return; + + del_timer(&link->release); + if (link->state & DEV_CONFIG) + bt950_release((u_long) link); + + if (link->handle) { + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + + /* Unlink device structure, free bits */ + *linkp = link->next; + + kfree(info); +} + + +static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +{ + int i; + + i = CardServices(fn, handle, tuple); + if (i != CS_SUCCESS) + return CS_NO_MORE_ITEMS; + + i = CardServices(GetTupleData, handle, tuple); + if (i != CS_SUCCESS) + return i; + + return CardServices(ParseTuple, handle, tuple, parse); +} + + +#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) +#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) + +static void bt950_config(dev_link_t *link) +{ + static ioaddr_t base[4] = { 0x2f8, 0x3e8, 0x2e8, 0x0 }; + client_handle_t handle = link->handle; + bt950_info_t *info = link->priv; + tuple_t tuple; + u_short buf[256]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + config_info_t config; + int i, j, try, last_ret, last_fn; + + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + + /* Get configuration register information */ + tuple.DesiredTuple = CISTPL_CONFIG; + last_ret = first_tuple(handle, &tuple, &parse); + if (last_ret != CS_SUCCESS) { + last_fn = ParseTuple; + goto cs_failed; + } + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + i = CardServices(GetConfigurationInfo, handle, &config); + link->conf.Vcc = config.Vcc; + + /* First pass: look for a config entry that looks normal. */ + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + /* Two tries: without IO aliases, then with aliases */ + for (try = 0; try < 2; try++) { + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if (i != CS_SUCCESS) + goto next_entry; + if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) + goto found_port; + } +next_entry: + i = next_tuple(handle, &tuple, &parse); + } + } + + /* Second pass: try to find an entry that isn't picky about + its base address, then try to grab any standard serial port + address, and finally try to get any free port. */ + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if ((i == CS_SUCCESS) && (cf->io.nwin > 0) + && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { + link->conf.ConfigIndex = cf->index; + for (j = 0; j < 5; j++) { + link->io.BasePort1 = base[j]; + link->io.IOAddrLines = base[j] ? 16 : 3; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) + goto found_port; + } + } + i = next_tuple(handle, &tuple, &parse); + } + +found_port: + if (i != CS_SUCCESS) { + printk(KERN_ERR "bt950_cs: No usable port range found. Giving up.\n"); + cs_error(link->handle, RequestIO, i); + goto failed; + } + + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIRQ, i); + link->irq.AssignedIRQ = 0; + } + + i = CardServices(RequestConfiguration, link->handle, &link->conf); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, i); + goto failed; + } + + MOD_INC_USE_COUNT; + + if (bt950_open(info) != 0) + goto failed; + + strcpy(info->node.dev_name, info->hdev.name); + link->dev = &info->node; + link->state &= ~DEV_CONFIG_PENDING; + + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + +failed: + bt950_release((u_long)link); + link->state &= ~DEV_CONFIG_PENDING; +} + + +static void bt950_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *) arg; + bt950_info_t *info = link->priv; + + if (link->state & DEV_PRESENT) + bt950_close(info); + + MOD_DEC_USE_COUNT; + + link->dev = NULL; + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; +} + + +static int bt950_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + bt950_info_t *info = link->priv; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + bt950_close(info); + link->state |= DEV_RELEASE_PENDING; + mod_timer(&link->release, jiffies + HZ / 20); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + bt950_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + bt950_stop_uart(info); + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + bt950_setup_uart(info); + } + break; + } + + return 0; +} + + + +/* ======================== Module initialization ======================== */ + + +int __init init_bt950_cs(void) +{ + servinfo_t serv; + int err; + + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "bt950_cs: Card Services release does not match!\n"); +// return -1; + } + + err = register_pccard_driver(&dev_info, &bt950_attach, &bt950_detach); + + return err; +} + + +void __exit exit_bt950_cs(void) +{ + unregister_pccard_driver(&dev_info); + + while (dev_list != NULL) + bt950_detach(dev_list); +} + + +module_init(init_bt950_cs); +module_exit(exit_bt950_cs); + +EXPORT_NO_SYMBOLS; diff -Nur linux-orig/drivers/bluetooth/Config.in linux/drivers/bluetooth/Config.in --- linux-orig/drivers/bluetooth/Config.in 2004-02-16 08:45:33.000000000 +0300 +++ linux/drivers/bluetooth/Config.in 2004-02-16 08:50:36.000000000 +0300 @@ -21,7 +21,7 @@ dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ -dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ +# dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ @@ -29,5 +29,7 @@ dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ +dep_tristate 'HCI BT950 (BT950 device) driver' CONFIG_BLUEZ_BT950 $CONFIG_BLUEZ + endmenu diff -Nur linux-orig/drivers/bluetooth/Makefile linux/drivers/bluetooth/Makefile --- linux-orig/drivers/bluetooth/Makefile 2004-02-16 08:45:33.000000000 +0300 +++ linux/drivers/bluetooth/Makefile 2004-02-16 08:50:47.000000000 +0300 @@ -17,10 +17,12 @@ obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o -obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o +# obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BLUEZ_HCIBTUART) += btuart_cs.o +obj-$(CONFIG_BLUEZ_BT950) += bt950_cs.o + include $(TOPDIR)/Rules.make hci_uart.o: $(uart-y)