📄 bluecard_cs.c
字号:
/* * * 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/delay.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("Bluetooth 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(dev_link_t *link);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_PCCARD_ID, &(info->hw_state))) return; 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_PCCARD_ID, &(info->hw_state))) return; 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) { BT_ERR("Unknown device"); 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) { BT_ERR("Unknown device"); 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 = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { BT_ERR("Can't allocate mem for new packet"); 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 */ BT_ERR("Unknown HCI packet with type 0x%02x received", 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; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: eh = (struct 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 = (struct 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 = (struct 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;}static irqreturn_t 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 || !info->hdev) { BT_ERR("Call of irq %d for unknown device", irq); return IRQ_NONE; } if (!test_bit(CARD_READY, &(info->hw_state))) return IRQ_HANDLED; 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -