lkml.org 
[lkml]   [2002]   [Jul]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subject[PATCH] Bluetooth Subsystem PC Card drivers for 2.4.19-rc2
From
Date
Hi Marcelo, 

this patch updates the PC Card drivers of the Bluetooth subsystem. It
modifies the following files:

linux-2.4.19-rc2/CREDITS
linux-2.4.19-rc2/MAINTAINERS
linux-2.4.19-rc2/Documentation/Configure.help
linux-2.4.19-rc2/drivers/bluetooth/Config.in
linux-2.4.19-rc2/drivers/bluetooth/Makefile
linux-2.4.19-rc2/drivers/bluetooth/bluecard_cs.c
linux-2.4.19-rc2/drivers/bluetooth/bt3c_cs.c
linux-2.4.19-rc2/drivers/bluetooth/dtl1_cs.c

All three drivers are complete independent and has been tested for a
long time. The drivers are stable and ready for the 2.4 kernel.

With these drivers in the final 2.4.19 Kernel, it will be very easy for
the Linux users to take a step into the Bluetooth world.

Regards

Marcel

diff -urN linux-2.4.19-rc2/CREDITS linux-2.4.19-rc2-mh/CREDITS
--- linux-2.4.19-rc2/CREDITS Wed Jul 17 02:41:57 2002
+++ linux-2.4.19-rc2-mh/CREDITS Wed Jul 17 02:43:38 2002
@@ -1329,6 +1329,12 @@
S: Provo, Utah 84606-5607
S: USA

+N: Marcel Holtmann
+E: marcel@holtmann.org
+W: http://www.holtmann.org
+D: Author of the Linux Bluetooth Subsystem PC Card drivers
+S: Germany
+
N: Rob W. W. Hooft
E: hooft@EMBL-Heidelberg.DE
D: Shared libs for graphics-tools and for the f2c compiler
diff -urN linux-2.4.19-rc2/Documentation/Configure.help linux-2.4.19-rc2-mh/Documentation/Configure.help
--- linux-2.4.19-rc2/Documentation/Configure.help Wed Jul 17 02:41:57 2002
+++ linux-2.4.19-rc2-mh/Documentation/Configure.help Wed Jul 17 02:43:38 2002
@@ -20494,10 +20494,36 @@
Bluetooth HCI DTL1 (PC Card) driver.
This driver provides support for Bluetooth PCMCIA devices with
Nokia DTL1 interface:
- Nokia Bluetooth PC Card, Socketcom Bluetooth CF module
+ Nokia Bluetooth Card
+ Socket Bluetooth CF Card

Say Y here to compile support for HCI DTL1 devices into the
kernel or say M to compile it as module (dtl1_cs.o).
+
+HCI BT3C (PC Card) device driver
+CONFIG_BLUEZ_HCIBT3C
+ Bluetooth HCI BT3C (PC Card) driver.
+ This driver provides support for Bluetooth PCMCIA devices with
+ 3Com BT3C interface:
+ 3Com Bluetooth Card (3CRWB6096)
+ HP Bluetooth Card
+
+ The HCI BT3C driver uses external firmware loader program provided in
+ the BlueFW package. For more information, see <http://bluez.sf.net>.
+
+ Say Y here to compile support for HCI BT3C devices into the
+ kernel or say M to compile it as module (bt3c_cs.o).
+
+HCI BlueCard (PC Card) device driver
+CONFIG_BLUEZ_HCIBLUECARD
+ Bluetooth HCI BlueCard (PC Card) driver.
+ This driver provides support for Bluetooth PCMCIA devices with
+ Anycom BlueCard interface:
+ Anycom Bluetooth PC Card
+ Anycom Bluetooth CF Card
+
+ Say Y here to compile support for HCI BlueCard devices into the
+ kernel or say M to compile it as module (bluecard_cs.o).

# The following options are for Linux when running on the Hitachi
# SuperH family of RISC microprocessors.
diff -urN linux-2.4.19-rc2/MAINTAINERS linux-2.4.19-rc2-mh/MAINTAINERS
--- linux-2.4.19-rc2/MAINTAINERS Wed Jul 17 02:41:58 2002
+++ linux-2.4.19-rc2-mh/MAINTAINERS Wed Jul 17 02:43:38 2002
@@ -266,6 +266,12 @@
W: http://bluez.sf.net
S: Maintained

+BLUETOOTH SUBSYSTEM (PC Card Drivers)
+P: Marcel Holtmann
+M: marcel@holtmann.org
+W: http://www.holtmann.org/linux/bluetooth/
+S: Maintained
+
BTTV VIDEO4LINUX DRIVER
P: Gerd Knorr
M: kraxel@bytesex.org
diff -urN linux-2.4.19-rc2/drivers/bluetooth/Config.in linux-2.4.19-rc2-mh/drivers/bluetooth/Config.in
--- linux-2.4.19-rc2/drivers/bluetooth/Config.in Wed Jul 17 02:42:04 2002
+++ linux-2.4.19-rc2-mh/drivers/bluetooth/Config.in Wed Jul 17 02:43:38 2002
@@ -14,6 +14,10 @@

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 BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ
+
dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ

endmenu
diff -urN linux-2.4.19-rc2/drivers/bluetooth/Makefile linux-2.4.19-rc2-mh/drivers/bluetooth/Makefile
--- linux-2.4.19-rc2/drivers/bluetooth/Makefile Wed Jul 17 02:42:04 2002
+++ linux-2.4.19-rc2-mh/drivers/bluetooth/Makefile Wed Jul 17 02:43:38 2002
@@ -14,6 +14,8 @@
uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o

obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o
+obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o
+obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o

include $(TOPDIR)/Rules.make

diff -urN linux-2.4.19-rc2/drivers/bluetooth/bluecard_cs.c linux-2.4.19-rc2-mh/drivers/bluetooth/bluecard_cs.c
--- linux-2.4.19-rc2/drivers/bluetooth/bluecard_cs.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.19-rc2-mh/drivers/bluetooth/bluecard_cs.c Wed Jul 17 02:43:38 2002
@@ -0,0 +1,1113 @@
+/*
+ *
+ * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.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>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0x86bc;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ driver for the Anycom BlueCard (LSE039/LSE041)");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bluecard_info_t {
+ dev_link_t link;
+ dev_node_t node;
+
+ struct hci_dev hdev;
+
+ spinlock_t lock; /* For serializing operations */
+ struct timer_list timer; /* For LED control */
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+
+ unsigned char ctrl_reg;
+ unsigned long hw_state; /* Status of the hardware and LED control */
+} bluecard_info_t;
+
+
+void bluecard_config(dev_link_t *link);
+void bluecard_release(u_long arg);
+int bluecard_event(event_t event, int priority, event_callback_args_t *args);
+
+static dev_info_t dev_info = "bluecard_cs";
+
+dev_link_t *bluecard_attach(void);
+void bluecard_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE 230400
+
+
+/* Hardware states */
+#define CARD_READY 1
+#define CARD_HAS_PCCARD_ID 4
+#define CARD_HAS_POWER_LED 5
+#define CARD_HAS_ACTIVITY_LED 6
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */
+#define XMIT_BUF_ONE_READY 6
+#define XMIT_BUF_TWO_READY 7
+#define XMIT_SENDING_READY 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
+
+
+/* These are the register offsets */
+#define REG_COMMAND 0x20
+#define REG_INTERRUPT 0x21
+#define REG_CONTROL 0x22
+#define REG_RX_CONTROL 0x24
+#define REG_CARD_RESET 0x30
+#define REG_LED_CTRL 0x30
+
+/* REG_COMMAND */
+#define REG_COMMAND_TX_BUF_ONE 0x01
+#define REG_COMMAND_TX_BUF_TWO 0x02
+#define REG_COMMAND_RX_BUF_ONE 0x04
+#define REG_COMMAND_RX_BUF_TWO 0x08
+#define REG_COMMAND_RX_WIN_ONE 0x00
+#define REG_COMMAND_RX_WIN_TWO 0x10
+
+/* REG_CONTROL */
+#define REG_CONTROL_BAUD_RATE_57600 0x00
+#define REG_CONTROL_BAUD_RATE_115200 0x01
+#define REG_CONTROL_BAUD_RATE_230400 0x02
+#define REG_CONTROL_BAUD_RATE_460800 0x03
+#define REG_CONTROL_RTS 0x04
+#define REG_CONTROL_BT_ON 0x08
+#define REG_CONTROL_BT_RESET 0x10
+#define REG_CONTROL_BT_RES_PU 0x20
+#define REG_CONTROL_INTERRUPT 0x40
+#define REG_CONTROL_CARD_RESET 0x80
+
+/* REG_RX_CONTROL */
+#define RTS_LEVEL_SHIFT_BITS 0x02
+
+
+
+/* ======================== LED handling routines ======================== */
+
+
+void bluecard_activity_led_timeout(u_long arg)
+{
+ bluecard_info_t *info = (bluecard_info_t *)arg;
+ unsigned int iobase = info->link.io.BasePort1;
+
+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+ /* Disable activity LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+ } else {
+ /* Disable power LED */
+ outb(0x00, iobase + 0x30);
+ }
+}
+
+
+static void bluecard_enable_activity_led(bluecard_info_t *info)
+{
+ unsigned int iobase = info->link.io.BasePort1;
+
+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+ /* Enable activity LED */
+ outb(0x10 | 0x40, iobase + 0x30);
+
+ /* Stop the LED after HZ/4 */
+ mod_timer(&(info->timer), jiffies + HZ / 4);
+ } else {
+ /* Enable power LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+
+ /* Stop the LED after HZ/2 */
+ mod_timer(&(info->timer), jiffies + HZ / 2);
+ }
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
+{
+ int i, actual;
+
+ actual = (len > 15) ? 15 : len;
+
+ outb_p(actual, iobase + offset);
+
+ for (i = 0; i < actual; i++)
+ outb_p(buf[i], iobase + offset + i + 1);
+
+ return actual;
+}
+
+
+static void bluecard_write_wakeup(bluecard_info_t *info)
+{
+ if (!info) {
+ printk(KERN_WARNING "bluecard_cs: Call of write_wakeup for unknown device.\n");
+ return;
+ }
+
+ if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+ 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 unsigned int offset;
+ register unsigned char command;
+ register unsigned long ready_bit;
+ register struct sk_buff *skb;
+ register int len;
+
+ clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (!(info->link.state & DEV_PRESENT))
+ return;
+
+ if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
+ if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
+ break;
+ offset = 0x10;
+ command = REG_COMMAND_TX_BUF_TWO;
+ ready_bit = XMIT_BUF_TWO_READY;
+ } else {
+ if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
+ break;
+ offset = 0x00;
+ command = REG_COMMAND_TX_BUF_ONE;
+ ready_bit = XMIT_BUF_ONE_READY;
+ }
+
+ if (!(skb = skb_dequeue(&(info->txq))))
+ break;
+
+ if (skb->pkt_type & 0x80) {
+ /* Disable RTS */
+ info->ctrl_reg |= REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+ }
+
+ /* Activate LED */
+ bluecard_enable_activity_led(info);
+
+ /* Send frame */
+ len = bluecard_write(iobase, offset, skb->data, skb->len);
+
+ /* Tell the FPGA to send the data */
+ outb_p(command, iobase + REG_COMMAND);
+
+ /* Mark the buffer as dirty */
+ clear_bit(ready_bit, &(info->tx_state));
+
+ if (skb->pkt_type & 0x80) {
+
+ wait_queue_head_t wait;
+ unsigned char baud_reg;
+
+ switch (skb->pkt_type) {
+ case PKT_BAUD_RATE_460800:
+ baud_reg = REG_CONTROL_BAUD_RATE_460800;
+ break;
+ case PKT_BAUD_RATE_230400:
+ baud_reg = REG_CONTROL_BAUD_RATE_230400;
+ break;
+ case PKT_BAUD_RATE_115200:
+ baud_reg = REG_CONTROL_BAUD_RATE_115200;
+ break;
+ case PKT_BAUD_RATE_57600:
+ /* Fall through... */
+ default:
+ baud_reg = REG_CONTROL_BAUD_RATE_57600;
+ break;
+ }
+
+ /* Wait until the command reaches the baseband */
+ init_waitqueue_head(&wait);
+ interruptible_sleep_on_timeout(&wait, HZ / 10);
+
+ /* Set baud on baseband */
+ info->ctrl_reg &= ~0x03;
+ info->ctrl_reg |= baud_reg;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable RTS */
+ info->ctrl_reg &= ~REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* 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;
+
+ /* Change buffer */
+ change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
+
+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
+{
+ int i, n, len;
+
+ outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
+
+ len = inb(iobase + offset);
+ n = 0;
+ i = 1;
+
+ while (n < len) {
+
+ if (i == 16) {
+ outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
+ i = 0;
+ }
+
+ buf[n] = inb(iobase + offset + i);
+
+ n++;
+ i++;
+
+ }
+
+ return len;
+}
+
+
+static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
+{
+ unsigned int iobase;
+ unsigned char buf[31];
+ int i, len;
+
+ if (!info) {
+ printk(KERN_WARNING "bluecard_cs: Call of receive for unknown device.\n");
+ return;
+ }
+
+ iobase = info->link.io.BasePort1;
+
+ if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+ bluecard_enable_activity_led(info);
+
+ len = bluecard_read(iobase, offset, buf, sizeof(buf));
+
+ for (i = 0; i < len; i++) {
+
+ /* 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_WARNING "bluecard_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 = buf[i];
+
+ switch (info->rx_skb->pkt_type) {
+
+ case 0x00:
+ /* init packet */
+ if (offset != 0x00) {
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ set_bit(XMIT_SENDING_READY, &(info->tx_state));
+ bluecard_write_wakeup(info);
+ }
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ 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 "bluecard_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) = buf[i];
+ 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;
+
+ }
+
+ }
+
+ }
+
+
+ }
+
+ info->hdev.stat.byte_rx += len;
+}
+
+
+void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+ bluecard_info_t *info = dev_inst;
+ unsigned int iobase;
+ unsigned char reg;
+
+ if (!info) {
+ printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (!test_bit(CARD_READY, &(info->hw_state)))
+ return;
+
+ iobase = info->link.io.BasePort1;
+
+ spin_lock(&(info->lock));
+
+ /* Disable interrupt */
+ info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ reg = inb(iobase + REG_INTERRUPT);
+
+ if ((reg != 0x00) && (reg != 0xff)) {
+
+ if (reg & 0x04) {
+ bluecard_receive(info, 0x00);
+ outb(0x04, iobase + REG_INTERRUPT);
+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+ }
+
+ if (reg & 0x08) {
+ bluecard_receive(info, 0x10);
+ outb(0x08, iobase + REG_INTERRUPT);
+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+ }
+
+ if (reg & 0x01) {
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ outb(0x01, iobase + REG_INTERRUPT);
+ bluecard_write_wakeup(info);
+ }
+
+ if (reg & 0x02) {
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ outb(0x02, iobase + REG_INTERRUPT);
+ bluecard_write_wakeup(info);
+ }
+
+ }
+
+ /* Enable interrupt */
+ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ spin_unlock(&(info->lock));
+}
+
+
+
+/* ======================== Device specific HCI commands ======================== */
+
+
+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
+{
+ bluecard_info_t *info = (bluecard_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 "bluecard_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:
+ 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);
+
+ bluecard_write_wakeup(info);
+
+ return 0;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bluecard_hci_flush(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int bluecard_hci_open(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+ unsigned int iobase = info->link.io.BasePort1;
+
+ bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
+
+ if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ /* Enable LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+
+ return 0;
+}
+
+
+static int bluecard_hci_close(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+ unsigned int iobase = info->link.io.BasePort1;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ bluecard_hci_flush(hdev);
+
+ /* Disable LED */
+ outb(0x00, iobase + 0x30);
+
+ return 0;
+}
+
+
+static int bluecard_hci_send_frame(struct sk_buff *skb)
+{
+ bluecard_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+ if (!hdev) {
+ printk(KERN_WARNING "bluecard_cs: Frame for unknown HCI device (hdev=NULL).");
+ return -ENODEV;
+ }
+
+ info = (bluecard_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);
+
+ bluecard_write_wakeup(info);
+
+ return 0;
+}
+
+
+static void bluecard_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+int bluecard_open(bluecard_info_t *info)
+{
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev;
+ unsigned char id;
+
+ spin_lock_init(&(info->lock));
+
+ init_timer(&(info->timer));
+ info->timer.function = &bluecard_activity_led_timeout;
+ info->timer.data = (u_long)info;
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ info->rx_skb = NULL;
+
+ id = inb(iobase + 0x30);
+
+ if ((id & 0x0f) == 0x02)
+ set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
+
+ if (id & 0x10)
+ set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
+
+ if (id & 0x20)
+ set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
+
+ /* Reset card */
+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Turn FPGA off */
+ outb(0x80, iobase + 0x30);
+
+ /* Wait some time */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 100);
+
+ /* Turn FPGA on */
+ outb(0x00, iobase + 0x30);
+
+ /* Activate card */
+ info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable interrupt */
+ outb(0xff, iobase + REG_INTERRUPT);
+ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Start the RX buffers */
+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+
+ /* Signal that the hardware is ready */
+ set_bit(CARD_READY, &(info->hw_state));
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ /* Control the point at which RTS is enabled */
+ outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
+
+ /* Timeout before it is safe to send the first HCI packet */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((HZ * 5) / 4); // or set it to 3/2
+
+
+ /* Initialize and register HCI device */
+
+ hdev = &(info->hdev);
+
+ hdev->type = HCI_PCCARD;
+ hdev->driver_data = info;
+
+ hdev->open = bluecard_hci_open;
+ hdev->close = bluecard_hci_close;
+ hdev->flush = bluecard_hci_flush;
+ hdev->send = bluecard_hci_send_frame;
+ hdev->destruct = bluecard_hci_destruct;
+ hdev->ioctl = bluecard_hci_ioctl;
+
+ if (hci_register_dev(hdev) < 0) {
+ printk(KERN_WARNING "bluecard_cs: Can't register HCI device %s.\n", hdev->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+int bluecard_close(bluecard_info_t *info)
+{
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
+ bluecard_hci_close(hdev);
+
+ clear_bit(CARD_READY, &(info->hw_state));
+
+ /* Reset card */
+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Turn FPGA off */
+ outb(0x80, iobase + 0x30);
+
+ if (hci_unregister_dev(hdev) < 0)
+ printk(KERN_WARNING "bluecard_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);
+}
+
+
+dev_link_t *bluecard_attach(void)
+{
+ bluecard_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 = &bluecard_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 = bluecard_interrupt;
+ link->irq.Instance = info;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* 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 = &bluecard_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);
+ bluecard_detach(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+
+void bluecard_detach(dev_link_t *link)
+{
+ bluecard_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)
+ bluecard_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)
+
+void bluecard_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ bluecard_info_t *info = link->priv;
+ tuple_t tuple;
+ u_short buf[256];
+ cisparse_t parse;
+ config_info_t config;
+ int i, n, 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;
+
+ link->conf.ConfigIndex = 0x20;
+ link->io.NumPorts1 = 64;
+ link->io.IOAddrLines = 6;
+
+ for (n = 0; n < 0x400; n += 0x40) {
+ link->io.BasePort1 = n ^ 0x300;
+ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+
+ if (i != CS_SUCCESS) {
+ 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 (bluecard_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:
+ bluecard_release((u_long)link);
+}
+
+
+void bluecard_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+ bluecard_info_t *info = link->priv;
+
+ if (link->state & DEV_PRESENT)
+ bluecard_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;
+}
+
+
+int bluecard_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ bluecard_info_t *info = link->priv;
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ bluecard_close(info);
+ mod_timer(&link->release, jiffies + HZ / 20);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ bluecard_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ CardServices(ReleaseConfiguration, link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (DEV_OK(link))
+ CardServices(RequestConfiguration, link->handle, &link->conf);
+ break;
+ }
+
+ return 0;
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_bluecard_cs(void)
+{
+ servinfo_t serv;
+ int err;
+
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "bluecard_cs: Card Services release does not match!\n");
+ return -1;
+ }
+
+ err = register_pccard_driver(&dev_info, &bluecard_attach, &bluecard_detach);
+
+ return err;
+}
+
+
+void __exit exit_bluecard_cs(void)
+{
+ unregister_pccard_driver(&dev_info);
+
+ while (dev_list != NULL)
+ bluecard_detach(dev_list);
+}
+
+
+module_init(init_bluecard_cs);
+module_exit(exit_bluecard_cs);
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.19-rc2/drivers/bluetooth/bt3c_cs.c linux-2.4.19-rc2-mh/drivers/bluetooth/bt3c_cs.c
--- linux-2.4.19-rc2/drivers/bluetooth/bt3c_cs.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.19-rc2-mh/drivers/bluetooth/bt3c_cs.c Wed Jul 17 02:43:38 2002
@@ -0,0 +1,946 @@
+/*
+ *
+ * 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>
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.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>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>");
+MODULE_DESCRIPTION("BlueZ 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;
+
+
+void bt3c_config(dev_link_t *link);
+void bt3c_release(u_long arg);
+int bt3c_event(event_t event, int priority, event_callback_args_t *args);
+
+static dev_info_t dev_info = "bt3c_cs";
+
+dev_link_t *bt3c_attach(void);
+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
+
+
+inline void bt3c_address(unsigned int iobase, unsigned short addr)
+{
+ outb(addr & 0xff, iobase + ADDR_L);
+ outb((addr >> 8) & 0xff, iobase + ADDR_H);
+}
+
+
+inline void bt3c_put(unsigned int iobase, unsigned short value)
+{
+ outb(value & 0xff, iobase + DATA_L);
+ outb((value >> 8) & 0xff, iobase + DATA_H);
+}
+
+
+inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
+{
+ bt3c_address(iobase, addr);
+ bt3c_put(iobase, value);
+}
+
+
+inline unsigned short bt3c_get(unsigned int iobase)
+{
+ unsigned short value = inb(iobase + DATA_L);
+
+ value |= inb(iobase + DATA_H) << 8;
+
+ return value;
+}
+
+
+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, int from)
+{
+ unsigned long flags;
+
+ if (!info) {
+ printk(KERN_WARNING "bt3c_cs: Call of write_wakeup for unknown device.\n");
+ return;
+ }
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
+ return;
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ 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->data, skb->len);
+
+ if (len != skb->len) {
+ printk(KERN_WARNING "bt3c_cs: very strange\n");
+ }
+
+ kfree_skb(skb);
+
+ info->hdev.stat.byte_tx += len;
+
+ } while (0);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+}
+
+
+static void bt3c_receive(bt3c_info_t *info)
+{
+ unsigned int iobase;
+ int size = 0, avail;
+
+ if (!info) {
+ printk(KERN_WARNING "bt3c_cs: Call of receive for unknown device.\n");
+ return;
+ }
+
+ iobase = info->link.io.BasePort1;
+
+ avail = bt3c_read(iobase, 0x7006);
+ //printk("bt3c_cs: receiving %d bytes\n", avail);
+
+ bt3c_address(iobase, 0x7480);
+ while (size < avail) {
+ size++;
+ 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_WARNING "bt3c_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 + DATA_L);
+ inb(iobase + DATA_H);
+ //printk("bt3c: PACKET_TYPE=%02x\n", info->rx_skb->pkt_type);
+
+ 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 "bt3c_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+ info->hdev.stat.err_rx++;
+ clear_bit(HCI_RUNNING, &(info->hdev.flags));
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ } else {
+
+ __u8 x = inb(iobase + DATA_L);
+
+ *skb_put(info->rx_skb, 1) = x;
+ inb(iobase + DATA_H);
+ 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;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ bt3c_io_write(iobase, 0x7006, 0x0000);
+}
+
+
+void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+ bt3c_info_t *info = dev_inst;
+ unsigned int iobase;
+ int iir;
+
+ if (!info) {
+ printk(KERN_WARNING "bt3c_cs: Call of irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ iobase = info->link.io.BasePort1;
+
+ spin_lock(&(info->lock));
+
+ iir = inb(iobase + CONTROL);
+ if (iir & 0x80) {
+ int stat = bt3c_read(iobase, 0x7001);
+
+ if ((stat & 0xff) == 0x7f) {
+ printk(KERN_WARNING "bt3c_cs: STRANGE stat=%04x\n", stat);
+ } else if ((stat & 0xff) != 0xff) {
+ if (stat & 0x0020) {
+ int stat = bt3c_read(iobase, 0x7002) & 0x10;
+ printk(KERN_WARNING "bt3c_cs: antena %s\n", stat ? "OUT" : "IN");
+ }
+ if (stat & 0x0001)
+ bt3c_receive(info);
+ if (stat & 0x0002) {
+ //printk("bt3c_cs: ACK %04x\n", stat);
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+ bt3c_write_wakeup(info, 1);
+ }
+
+ bt3c_io_write(iobase, 0x7001, 0x0000);
+
+ outb(iir, iobase + CONTROL);
+ }
+ }
+
+ spin_unlock(&(info->lock));
+}
+
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bt3c_hci_flush(struct hci_dev *hdev)
+{
+ bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int bt3c_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));
+
+ return 0;
+}
+
+
+static int bt3c_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ bt3c_hci_flush(hdev);
+
+ return 0;
+}
+
+
+static int bt3c_hci_send_frame(struct sk_buff *skb)
+{
+ bt3c_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+ if (!hdev) {
+ printk(KERN_WARNING "bt3c_cs: Frame for unknown HCI device (hdev=NULL).");
+ return -ENODEV;
+ }
+
+ info = (bt3c_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);
+
+ bt3c_write_wakeup(info, 0);
+
+ return 0;
+}
+
+
+static void bt3c_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== User mode firmware loader ======================== */
+
+
+#define FW_LOADER "/sbin/bluefw"
+static int errno;
+
+
+static int bt3c_fw_loader_exec(void *dev)
+{
+ char *argv[] = { FW_LOADER, "pccard", dev, NULL };
+ char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+ int err;
+
+ err = exec_usermodehelper(FW_LOADER, argv, envp);
+ if (err)
+ printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev);
+
+ return err;
+}
+
+
+static int bt3c_firmware_load(bt3c_info_t *info)
+{
+ sigset_t tmpsig;
+ char dev[16];
+ pid_t pid;
+ int result;
+
+ /* Check if root fs is mounted */
+ if (!current->fs->root) {
+ printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n");
+ return -EPERM;
+ }
+
+ sprintf(dev, "%04x", info->link.io.BasePort1);
+
+ pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0);
+ if (pid < 0) {
+ printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid);
+ return pid;
+ }
+
+ /* Block signals, everything but SIGKILL/SIGSTOP */
+ spin_lock_irq(&current->sigmask_lock);
+ tmpsig = current->blocked;
+ siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ result = waitpid(pid, NULL, __WCLONE);
+
+ /* Allow signals again */
+ spin_lock_irq(&current->sigmask_lock);
+ current->blocked = tmpsig;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (result != pid) {
+ printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result);
+ return -result;
+ }
+
+ return 0;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+int bt3c_open(bt3c_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;
+
+ /* Load firmware */
+
+ if ((err = bt3c_firmware_load(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 = bt3c_hci_open;
+ hdev->close = bt3c_hci_close;
+ hdev->flush = bt3c_hci_flush;
+ hdev->send = bt3c_hci_send_frame;
+ hdev->destruct = bt3c_hci_destruct;
+ hdev->ioctl = bt3c_hci_ioctl;
+
+ if (hci_register_dev(hdev) < 0) {
+ printk(KERN_WARNING "bt3c_cs: Can't register HCI device %s.\n", hdev->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+int bt3c_close(bt3c_info_t *info)
+{
+ struct hci_dev *hdev = &(info->hdev);
+
+ bt3c_hci_close(hdev);
+
+ if (hci_unregister_dev(hdev) < 0)
+ printk(KERN_WARNING "bt3c_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);
+}
+
+
+dev_link_t *bt3c_attach(void)
+{
+ bt3c_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 = &bt3c_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 = bt3c_interrupt;
+ link->irq.Instance = info;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* 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 = &bt3c_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);
+ bt3c_detach(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+
+void bt3c_detach(dev_link_t *link)
+{
+ bt3c_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)
+ bt3c_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)
+
+void bt3c_config(dev_link_t *link)
+{
+ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ client_handle_t handle = link->handle;
+ bt3c_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_NOTICE "bt3c_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 (bt3c_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:
+ bt3c_release((u_long)link);
+}
+
+
+void bt3c_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+ bt3c_info_t *info = link->priv;
+
+ if (link->state & DEV_PRESENT)
+ bt3c_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;
+}
+
+
+int bt3c_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ bt3c_info_t *info = link->priv;
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ bt3c_close(info);
+ mod_timer(&link->release, jiffies + HZ / 20);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ bt3c_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ CardServices(ReleaseConfiguration, link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (DEV_OK(link))
+ CardServices(RequestConfiguration, link->handle, &link->conf);
+ break;
+ }
+
+ return 0;
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_bt3c_cs(void)
+{
+ servinfo_t serv;
+ int err;
+
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "bt3c_cs: Card Services release does not match!\n");
+ return -1;
+ }
+
+ err = register_pccard_driver(&dev_info, &bt3c_attach, &bt3c_detach);
+
+ return err;
+}
+
+
+void __exit exit_bt3c_cs(void)
+{
+ unregister_pccard_driver(&dev_info);
+
+ while (dev_list != NULL)
+ bt3c_detach(dev_list);
+}
+
+
+module_init(init_bt3c_cs);
+module_exit(exit_bt3c_cs);
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.19-rc2/drivers/bluetooth/dtl1_cs.c linux-2.4.19-rc2-mh/drivers/bluetooth/dtl1_cs.c
--- linux-2.4.19-rc2/drivers/bluetooth/dtl1_cs.c Wed Jul 17 02:42:04 2002
+++ linux-2.4.19-rc2-mh/drivers/bluetooth/dtl1_cs.c Wed Jul 17 02:43:38 2002
@@ -75,23 +75,22 @@


typedef struct dtl1_info_t {
- dev_link_t link;
- dev_node_t node;
+ dev_link_t link;
+ dev_node_t node;

- struct hci_dev hdev;
+ struct hci_dev hdev;

- spinlock_t lock; /* For serializing operations */
+ spinlock_t lock; /* For serializing operations */

- unsigned long flowmask; /* HCI flow mask */
- int ri_latch;
+ unsigned long flowmask; /* HCI flow mask */
+ int ri_latch;

- struct sk_buff_head txq;
- unsigned long tx_state;
-
- unsigned long rx_state;
- unsigned long rx_count;
- struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+ unsigned long tx_state;

+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
} dtl1_info_t;


@@ -104,7 +103,7 @@
dev_link_t *dtl1_attach(void);
void dtl1_detach(dev_link_t *);

-dev_link_t *dev_list = NULL;
+static dev_link_t *dev_list = NULL;


/* Transmit states */
@@ -118,282 +117,253 @@


typedef struct {
- u8 type;
- u8 zero;
- u16 len;
-} __attribute__ ((packed)) nsh_t; /* Nokia Specific Header */
+ u8 type;
+ u8 zero;
+ u16 len;
+} __attribute__ ((packed)) nsh_t; /* Nokia Specific Header */

-#define NSHL 4 /* Nokia Specific Header Length */
+#define NSHL 4 /* Nokia Specific Header Length */



/* ======================== Interrupt handling ======================== */


-static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) {
-
- int actual = 0;
-
-
- /* Tx FIFO should be empty */
- if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
- 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 dtl1_write_wakeup(dtl1_info_t *info) {
-
- if (!info) {
- printk(KERN_WARNING "dtl1_cs: Call of write_wakeup for unknown device.\n");
- return;
- }
-
-
- if (test_bit(XMIT_WAITING, &(info->tx_state))) {
- set_bit(XMIT_WAKEUP, &(info->tx_state));
- 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;
-
-
- /* Send frame */
- len = dtl1_write(iobase, 32, skb->data, skb->len);
-
- if (len == skb->len) {
- set_bit(XMIT_WAITING, &(info->tx_state));
- 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 void dtl1_control(dtl1_info_t *info, struct sk_buff *skb) {
-
- u8 flowmask = *(u8 *)skb->data;
- int i;
-
-
- printk(KERN_INFO "dtl1_cs: Nokia control data = ");
- for (i = 0; i < skb->len; i++) {
- printk("%02x ", skb->data[i]);
- }
- printk("\n");
-
-
- /* transition to active state */
- if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
- clear_bit(XMIT_WAITING, &(info->tx_state));
- dtl1_write_wakeup(info);
- }
-
- info->flowmask = flowmask;
-
-
- kfree_skb(skb);
-
-}
-
-
-static void dtl1_receive(dtl1_info_t *info) {
-
- unsigned int iobase;
- nsh_t *nsh;
- int boguscount = 0;
-
-
- if (!info) {
- printk(KERN_WARNING "dtl1_cs: Call of receive for unknown device.\n");
- return;
- }
-
-
- iobase = info->link.io.BasePort1;
-
- do {
- info->hdev.stat.byte_rx++;
-
- /* Allocate packet */
- if (info->rx_skb == NULL)
- if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
- printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n");
- info->rx_state = RECV_WAIT_NSH;
- info->rx_count = NSHL;
- return;
- }
-
-
- *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
- nsh = (nsh_t *)info->rx_skb->data;
-
- info->rx_count--;
-
-
- if (info->rx_count == 0) {
-
- switch (info->rx_state) {
- case RECV_WAIT_NSH:
- info->rx_state = RECV_WAIT_DATA;
- info->rx_count = nsh->len + (nsh->len & 0x0001);
- break;
- case RECV_WAIT_DATA:
- info->rx_skb->pkt_type = nsh->type;
-
- /* remove PAD byte if it exists */
- if (nsh->len & 0x0001) {
- info->rx_skb->tail--;
- info->rx_skb->len--;
- }
-
- /* remove NSH */
- skb_pull(info->rx_skb, NSHL);
-
-
- switch (info->rx_skb->pkt_type) {
- case 0x80:
- /* control data for the Nokia Card */
- dtl1_control(info, info->rx_skb);
- break;
- case 0x82:
- case 0x83:
- case 0x84:
- /* send frame to the HCI layer */
- info->rx_skb->dev = (void *)&(info->hdev);
- info->rx_skb->pkt_type &= 0x0f;
- hci_recv_frame(info->rx_skb);
- break;
- default:
- /* unknown packet */
- printk(KERN_WARNING "dtl1_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
- kfree_skb(info->rx_skb);
- break;
- }
-
-
- info->rx_state = RECV_WAIT_NSH;
- info->rx_count = NSHL;
- info->rx_skb = NULL;
- break;
- }
-
- }
-
-
- /* Make sure we don't stay here to long */
- if (boguscount++ > 32)
- break;
-
- } while (inb(iobase + UART_LSR) & UART_LSR_DR);
-
-
-}
-
-
-void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs) {
-
- dtl1_info_t *info = dev_inst;
- unsigned int iobase;
- unsigned char msr;
- int boguscount = 0;
- int iir, lsr;
-
-
- if (!info) {
- printk(KERN_WARNING "dtl1_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) & UART_IIR_ID;
- while (iir) {
-
- /* Clear interrupt */
- lsr = inb(iobase + UART_LSR);
-
- switch (iir) {
- case UART_IIR_RLSI:
- printk(KERN_NOTICE "dtl1_cs: RLSI\n");
- break;
- case UART_IIR_RDI:
- /* Receive interrupt */
- dtl1_receive(info);
- break;
- case UART_IIR_THRI:
- if (lsr & UART_LSR_THRE) {
- /* Transmitter ready for data */
- dtl1_write_wakeup(info);
- }
- break;
- default:
- printk(KERN_NOTICE "dtl1_cs: Unhandled IIR=%#x\n", iir);
- break;
- }
-
- /* Make sure we don't stay here to long */
- if (boguscount++ > 100)
- break;
-
- iir = inb(iobase + UART_IIR) & UART_IIR_ID;
-
- }
-
-
- msr = inb(iobase + UART_MSR);
-
- if (info->ri_latch ^ (msr & UART_MSR_RI)) {
- info->ri_latch = msr & UART_MSR_RI;
- clear_bit(XMIT_WAITING, &(info->tx_state));
- dtl1_write_wakeup(info);
- }
-
- spin_unlock(&(info->lock));
+static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Tx FIFO should be empty */
+ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+ 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 dtl1_write_wakeup(dtl1_info_t *info)
+{
+ if (!info) {
+ printk(KERN_WARNING "dtl1_cs: Call of write_wakeup for unknown device.\n");
+ return;
+ }
+
+ if (test_bit(XMIT_WAITING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ 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;
+
+ /* Send frame */
+ len = dtl1_write(iobase, 32, skb->data, skb->len);
+
+ if (len == skb->len) {
+ set_bit(XMIT_WAITING, &(info->tx_state));
+ 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 void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
+{
+ u8 flowmask = *(u8 *)skb->data;
+ int i;
+
+ printk(KERN_INFO "dtl1_cs: Nokia control data = ");
+ for (i = 0; i < skb->len; i++) {
+ printk("%02x ", skb->data[i]);
+ }
+ printk("\n");
+
+ /* transition to active state */
+ if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
+ clear_bit(XMIT_WAITING, &(info->tx_state));
+ dtl1_write_wakeup(info);
+ }
+
+ info->flowmask = flowmask;
+
+ kfree_skb(skb);
+}
+
+
+static void dtl1_receive(dtl1_info_t *info)
+{
+ unsigned int iobase;
+ nsh_t *nsh;
+ int boguscount = 0;
+
+ if (!info) {
+ printk(KERN_WARNING "dtl1_cs: Call of receive for unknown device.\n");
+ return;
+ }
+
+ iobase = info->link.io.BasePort1;
+
+ do {
+ info->hdev.stat.byte_rx++;
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL)
+ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n");
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ return;
+ }
+
+ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+ nsh = (nsh_t *)info->rx_skb->data;
+
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ switch (info->rx_state) {
+ case RECV_WAIT_NSH:
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = nsh->len + (nsh->len & 0x0001);
+ break;
+ case RECV_WAIT_DATA:
+ info->rx_skb->pkt_type = nsh->type;
+
+ /* remove PAD byte if it exists */
+ if (nsh->len & 0x0001) {
+ info->rx_skb->tail--;
+ info->rx_skb->len--;
+ }
+
+ /* remove NSH */
+ skb_pull(info->rx_skb, NSHL);
+
+ switch (info->rx_skb->pkt_type) {
+ case 0x80:
+ /* control data for the Nokia Card */
+ dtl1_control(info, info->rx_skb);
+ break;
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ /* send frame to the HCI layer */
+ info->rx_skb->dev = (void *)&(info->hdev);
+ info->rx_skb->pkt_type &= 0x0f;
+ hci_recv_frame(info->rx_skb);
+ break;
+ default:
+ /* unknown packet */
+ printk(KERN_WARNING "dtl1_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+ kfree_skb(info->rx_skb);
+ break;
+ }
+
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ info->rx_skb = NULL;
+ break;
+ }
+
+ }
+
+ /* Make sure we don't stay here to long */
+ if (boguscount++ > 32)
+ break;
+
+ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+ dtl1_info_t *info = dev_inst;
+ unsigned int iobase;
+ unsigned char msr;
+ int boguscount = 0;
+ int iir, lsr;
+
+ if (!info) {
+ printk(KERN_WARNING "dtl1_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) & UART_IIR_ID;
+ while (iir) {
+
+ /* Clear interrupt */
+ lsr = inb(iobase + UART_LSR);
+
+ switch (iir) {
+ case UART_IIR_RLSI:
+ printk(KERN_NOTICE "dtl1_cs: RLSI\n");
+ break;
+ case UART_IIR_RDI:
+ /* Receive interrupt */
+ dtl1_receive(info);
+ break;
+ case UART_IIR_THRI:
+ if (lsr & UART_LSR_THRE) {
+ /* Transmitter ready for data */
+ dtl1_write_wakeup(info);
+ }
+ break;
+ default:
+ printk(KERN_NOTICE "dtl1_cs: Unhandled IIR=%#x\n", iir);
+ break;
+ }
+
+ /* Make sure we don't stay here to long */
+ if (boguscount++ > 100)
+ break;
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+ }
+
+ msr = inb(iobase + UART_MSR);
+
+ if (info->ri_latch ^ (msr & UART_MSR_RI)) {
+ info->ri_latch = msr & UART_MSR_RI;
+ clear_bit(XMIT_WAITING, &(info->tx_state));
+ dtl1_write_wakeup(info);
+ }

+ spin_unlock(&(info->lock));
}


@@ -401,107 +371,94 @@
/* ======================== HCI interface ======================== */


-static int dtl1_hci_open(struct hci_dev *hdev) {
-
- set_bit(HCI_RUNNING, &(hdev->flags));
-
-
- return 0;
+static int dtl1_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));

+ return 0;
}


-static int dtl1_hci_flush(struct hci_dev *hdev) {
-
- dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
-
-
- /* Drop TX queue */
- skb_queue_purge(&(info->txq));
-
+static int dtl1_hci_flush(struct hci_dev *hdev)
+{
+ dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);

- return 0;
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));

+ return 0;
}


-static int dtl1_hci_close(struct hci_dev *hdev) {
+static int dtl1_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;

- if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
- return 0;
-
-
- dtl1_hci_flush(hdev);
-
-
- return 0;
+ dtl1_hci_flush(hdev);

+ return 0;
}


-static int dtl1_hci_send_frame(struct sk_buff *skb) {
-
- dtl1_info_t *info;
- struct hci_dev* hdev = (struct hci_dev *)(skb->dev);
- struct sk_buff *s;
- nsh_t nsh;
-
+static int dtl1_hci_send_frame(struct sk_buff *skb)
+{
+ dtl1_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+ struct sk_buff *s;
+ nsh_t nsh;

- if (!hdev) {
- printk(KERN_WARNING "dtl1_cs: Frame for unknown HCI device (hdev=NULL).");
- return -ENODEV;
- }
+ if (!hdev) {
+ printk(KERN_WARNING "dtl1_cs: Frame for unknown HCI device (hdev=NULL).");
+ return -ENODEV;
+ }

- info = (dtl1_info_t *)(hdev->driver_data);
+ info = (dtl1_info_t *)(hdev->driver_data);

+ switch (skb->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ nsh.type = 0x81;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ nsh.type = 0x82;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ nsh.type = 0x83;
+ break;
+ };

- switch (skb->pkt_type) {
- case HCI_COMMAND_PKT:
- hdev->stat.cmd_tx++;
- nsh.type = 0x81;
- break;
- case HCI_ACLDATA_PKT:
- hdev->stat.acl_tx++;
- nsh.type = 0x82;
- break;
- case HCI_SCODATA_PKT:
- hdev->stat.sco_tx++;
- nsh.type = 0x83;
- break;
- };
+ nsh.zero = 0;
+ nsh.len = skb->len;

- nsh.zero = 0;
- nsh.len = skb->len;
+ s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
+ skb_reserve(s, NSHL);
+ memcpy(skb_put(s, skb->len), skb->data, skb->len);
+ if (skb->len & 0x0001)
+ *skb_put(s, 1) = 0; /* PAD */

- s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
- skb_reserve(s, NSHL);
- memcpy(skb_put(s, skb->len), skb->data, skb->len);
- if (skb->len & 0x0001)
- *skb_put(s, 1) = 0; /* PAD */
+ /* Prepend skb with Nokia frame header and queue */
+ memcpy(skb_push(s, NSHL), &nsh, NSHL);
+ skb_queue_tail(&(info->txq), s);

- /* Prepend skb with Nokia frame header and queue */
- memcpy(skb_push(s, NSHL), &nsh, NSHL);
- skb_queue_tail(&(info->txq), s);
+ dtl1_write_wakeup(info);

+ kfree_skb(skb);

- dtl1_write_wakeup(info);
-
- kfree_skb(skb);
-
-
- return 0;
-
+ return 0;
}


-static void dtl1_hci_destruct(struct hci_dev *hdev) {
+static void dtl1_hci_destruct(struct hci_dev *hdev)
+{
}


-static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) {
-
- return -ENOIOCTLCMD;
-
+static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
}


@@ -509,101 +466,91 @@
/* ======================== Card services HCI interaction ======================== */


-int dtl1_open(dtl1_info_t *info) {
+int dtl1_open(dtl1_info_t *info)
+{
+ unsigned long flags;
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev;

- unsigned long flags;
- unsigned int iobase = info->link.io.BasePort1;
- struct hci_dev *hdev;
+ spin_lock_init(&(info->lock));

+ skb_queue_head_init(&(info->txq));

- spin_lock_init(&(info->lock));
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ info->rx_skb = NULL;

- skb_queue_head_init(&(info->txq));
+ set_bit(XMIT_WAITING, &(info->tx_state));

- info->rx_state = RECV_WAIT_NSH;
- info->rx_count = NSHL;
- info->rx_skb = NULL;
+ spin_lock_irqsave(&(info->lock), flags);

- set_bit(XMIT_WAITING, &(info->tx_state));
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);

+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);

- spin_lock_irqsave(&(info->lock), flags);
+ /* Initialize UART */
+ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);

- /* Reset UART */
- outb(0, iobase + UART_MCR);
+ info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI;

- /* Turn off interrupts */
- outb(0, iobase + UART_IER);
+ /* Turn on interrupts */
+ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);

- /* Initialize UART */
- outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
- outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+ spin_unlock_irqrestore(&(info->lock), flags);

- info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI;
+ /* Timeout before it is safe to send the first HCI packet */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ * 2);

- /* Turn on interrupts */
- outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);

- spin_unlock_irqrestore(&(info->lock), flags);
+ /* Initialize and register HCI device */

+ hdev = &(info->hdev);

- /* Timeout before it is safe to send the first HCI packet */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ * 2);
+ hdev->type = HCI_PCCARD;
+ hdev->driver_data = info;

+ hdev->open = dtl1_hci_open;
+ hdev->close = dtl1_hci_close;
+ hdev->flush = dtl1_hci_flush;
+ hdev->send = dtl1_hci_send_frame;
+ hdev->destruct = dtl1_hci_destruct;
+ hdev->ioctl = dtl1_hci_ioctl;

- /* Initialize and register HCI device */
-
- hdev = &(info->hdev);
-
- hdev->type = HCI_PCCARD;
- hdev->driver_data = info;
-
- hdev->open = dtl1_hci_open;
- hdev->close = dtl1_hci_close;
- hdev->flush = dtl1_hci_flush;
- hdev->send = dtl1_hci_send_frame;
- hdev->destruct = dtl1_hci_destruct;
- hdev->ioctl = dtl1_hci_ioctl;
-
- if (hci_register_dev(hdev) < 0) {
- printk(KERN_WARNING "dtl1_cs: Can't register HCI device %s.\n", hdev->name);
- return -ENODEV;
- }
-
-
- return 0;
+ if (hci_register_dev(hdev) < 0) {
+ printk(KERN_WARNING "dtl1_cs: Can't register HCI device %s.\n", hdev->name);
+ return -ENODEV;
+ }

+ return 0;
}


-int dtl1_close(dtl1_info_t *info) {
-
- unsigned long flags;
- unsigned int iobase = info->link.io.BasePort1;
- struct hci_dev *hdev = &(info->hdev);
-
-
- dtl1_hci_close(hdev);
-
-
- spin_lock_irqsave(&(info->lock), flags);
-
- /* Reset UART */
- outb(0, iobase + UART_MCR);
+int dtl1_close(dtl1_info_t *info)
+{
+ unsigned long flags;
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);

- /* Turn off interrupts */
- outb(0, iobase + UART_IER);
+ dtl1_hci_close(hdev);

- spin_unlock_irqrestore(&(info->lock), flags);
+ spin_lock_irqsave(&(info->lock), flags);

+ /* Reset UART */
+ outb(0, iobase + UART_MCR);

- if (hci_unregister_dev(hdev) < 0)
- printk(KERN_WARNING "dtl1_cs: Can't unregister HCI device %s.\n", hdev->name);
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);

+ spin_unlock_irqrestore(&(info->lock), flags);

- return 0;
+ if (hci_unregister_dev(hdev) < 0)
+ printk(KERN_WARNING "dtl1_cs: Can't unregister HCI device %s.\n", hdev->name);

+ return 0;
}


@@ -611,291 +558,267 @@
/* ======================== Card services ======================== */


-static void cs_error(client_handle_t handle, int func, int ret) {
-
- error_info_t err = { func, ret };
-
-
- CardServices(ReportError, handle, &err);
-
-}
-
-
-dev_link_t *dtl1_attach(void) {
-
- dtl1_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 = &dtl1_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 = dtl1_interrupt;
- link->irq.Instance = info;
-
- link->conf.Attributes = CONF_ENABLE_IRQ;
- link->conf.Vcc = 50;
- link->conf.IntType = INT_MEMORY_AND_IO;
-
-
- /* 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 = &dtl1_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);
- dtl1_detach(link);
- return NULL;
- }
-
-
- return link;
-
-}
-
-
-void dtl1_detach(dev_link_t *link) {
-
- dtl1_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)
- dtl1_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);
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+
+ CardServices(ReportError, handle, &err);
+}
+
+
+dev_link_t *dtl1_attach(void)
+{
+ dtl1_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 = &dtl1_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 = dtl1_interrupt;
+ link->irq.Instance = info;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* 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 = &dtl1_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);
+ dtl1_detach(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+
+void dtl1_detach(dev_link_t *link)
+{
+ dtl1_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)
+ dtl1_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)

-void dtl1_config(dev_link_t *link) {
-
- client_handle_t handle = link->handle;
- dtl1_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, 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;
-
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-
- /* Look for a generic full-sized window */
- link->io.NumPorts1 = 8;
- i = first_tuple(handle, &tuple, &parse);
- while (i != CS_NO_MORE_ITEMS) {
- if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.NumPorts1 = cf->io.win[0].len; /*yo*/
- link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
- i = CardServices(RequestIO, link->handle, &link->io);
- if (i == CS_SUCCESS)
- break;
- }
- i = next_tuple(handle, &tuple, &parse);
- }
-
- if (i != CS_SUCCESS) {
- 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 (dtl1_open(info) != 0)
- goto failed;
-
-
- link->dev = &info->node;
- link->state &= ~DEV_CONFIG_PENDING;
-
-
- return;
+void dtl1_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ dtl1_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, 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;
+
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = 255;
+ tuple.Attributes = 0;
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+ /* Look for a generic full-sized window */
+ link->io.NumPorts1 = 8;
+ i = first_tuple(handle, &tuple, &parse);
+ while (i != CS_NO_MORE_ITEMS) {
+ if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
+ link->conf.ConfigIndex = cf->index;
+ link->io.BasePort1 = cf->io.win[0].base;
+ link->io.NumPorts1 = cf->io.win[0].len; /*yo */
+ link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+ i = next_tuple(handle, &tuple, &parse);
+ }
+
+ if (i != CS_SUCCESS) {
+ 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 (dtl1_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:
- dtl1_release((u_long)link);
+ cs_error(link->handle, last_fn, last_ret);

+failed:
+ dtl1_release((u_long)link);
}


-void dtl1_release(u_long arg) {
-
- dev_link_t *link = (dev_link_t *)arg;
- dtl1_info_t *info = link->priv;
-
-
- if (link->state & DEV_PRESENT)
- dtl1_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;
-
+void dtl1_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+ dtl1_info_t *info = link->priv;
+
+ if (link->state & DEV_PRESENT)
+ dtl1_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;
}


-int dtl1_event(event_t event, int priority, event_callback_args_t *args) {
-
- dev_link_t *link = args->client_data;
- dtl1_info_t *info = link->priv;
-
-
- switch (event) {
- case CS_EVENT_CARD_REMOVAL:
- link->state &= ~DEV_PRESENT;
- if (link->state & DEV_CONFIG) {
- dtl1_close(info);
- mod_timer(&link->release, jiffies + HZ/20);
- }
- break;
- case CS_EVENT_CARD_INSERTION:
- link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
- dtl1_config(link);
- break;
- case CS_EVENT_PM_SUSPEND:
- link->state |= DEV_SUSPEND;
- /* Fall through... */
- case CS_EVENT_RESET_PHYSICAL:
- if (link->state & DEV_CONFIG)
- CardServices(ReleaseConfiguration, link->handle);
- break;
- case CS_EVENT_PM_RESUME:
- link->state &= ~DEV_SUSPEND;
- /* Fall through... */
- case CS_EVENT_CARD_RESET:
- if (DEV_OK(link))
- CardServices(RequestConfiguration, link->handle, &link->conf);
- break;
- }
-
-
- return 0;
+int dtl1_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ dtl1_info_t *info = link->priv;
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ dtl1_close(info);
+ mod_timer(&link->release, jiffies + HZ / 20);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ dtl1_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ CardServices(ReleaseConfiguration, link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (DEV_OK(link))
+ CardServices(RequestConfiguration, link->handle, &link->conf);
+ break;
+ }

+ return 0;
}


@@ -903,33 +826,29 @@
/* ======================== Module initialization ======================== */


-int __init init_dtl1_cs(void) {
+int __init init_dtl1_cs(void)
+{
+ servinfo_t serv;
+ int err;

- servinfo_t serv;
- int err;
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n");
+ return -1;
+ }

+ err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach);

- CardServices(GetCardServicesInfo, &serv);
- if (serv.Revision != CS_RELEASE_CODE) {
- printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n");
- return -1;
- }
-
-
- err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach);
-
-
- return err;
-
+ return err;
}

-void __exit exit_dtl1_cs(void) {
-
- unregister_pccard_driver(&dev_info);

- while (dev_list != NULL)
- dtl1_detach(dev_list);
+void __exit exit_dtl1_cs(void)
+{
+ unregister_pccard_driver(&dev_info);

+ while (dev_list != NULL)
+ dtl1_detach(dev_list);
}

\
 
 \ /
  Last update: 2005-03-22 13:27    [from the cache]
©2003-2011 Jasper Spaans