162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Driver for the 3Com Bluetooth PCMCIA card 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> 662306a36Sopenharmony_ci * Jose Orlando Pereira <jop@di.uminho.pt> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 1162306a36Sopenharmony_ci * published by the Free Software Foundation; 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Software distributed under the License is distributed on an "AS 1462306a36Sopenharmony_ci * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 1562306a36Sopenharmony_ci * implied. See the License for the specific language governing 1662306a36Sopenharmony_ci * rights and limitations under the License. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * The initial developer of the original code is David A. Hinds 1962306a36Sopenharmony_ci * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 2062306a36Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/init.h> 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/types.h> 3062306a36Sopenharmony_ci#include <linux/delay.h> 3162306a36Sopenharmony_ci#include <linux/errno.h> 3262306a36Sopenharmony_ci#include <linux/ptrace.h> 3362306a36Sopenharmony_ci#include <linux/ioport.h> 3462306a36Sopenharmony_ci#include <linux/spinlock.h> 3562306a36Sopenharmony_ci#include <linux/moduleparam.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/skbuff.h> 3862306a36Sopenharmony_ci#include <linux/string.h> 3962306a36Sopenharmony_ci#include <linux/serial.h> 4062306a36Sopenharmony_ci#include <linux/serial_reg.h> 4162306a36Sopenharmony_ci#include <linux/bitops.h> 4262306a36Sopenharmony_ci#include <asm/io.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <linux/device.h> 4562306a36Sopenharmony_ci#include <linux/firmware.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <pcmcia/cistpl.h> 4862306a36Sopenharmony_ci#include <pcmcia/ciscode.h> 4962306a36Sopenharmony_ci#include <pcmcia/ds.h> 5062306a36Sopenharmony_ci#include <pcmcia/cisreg.h> 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 5362306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h> 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* ======================== Module parameters ======================== */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciMODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); 6162306a36Sopenharmony_ciMODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card"); 6262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6362306a36Sopenharmony_ciMODULE_FIRMWARE("BT3CPCC.bin"); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* ======================== Local structures ======================== */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct bt3c_info { 7162306a36Sopenharmony_ci struct pcmcia_device *p_dev; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci struct hci_dev *hdev; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci spinlock_t lock; /* For serializing operations */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci struct sk_buff_head txq; 7862306a36Sopenharmony_ci unsigned long tx_state; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci unsigned long rx_state; 8162306a36Sopenharmony_ci unsigned long rx_count; 8262306a36Sopenharmony_ci struct sk_buff *rx_skb; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int bt3c_config(struct pcmcia_device *link); 8762306a36Sopenharmony_cistatic void bt3c_release(struct pcmcia_device *link); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void bt3c_detach(struct pcmcia_device *p_dev); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* Transmit states */ 9362306a36Sopenharmony_ci#define XMIT_SENDING 1 9462306a36Sopenharmony_ci#define XMIT_WAKEUP 2 9562306a36Sopenharmony_ci#define XMIT_WAITING 8 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Receiver states */ 9862306a36Sopenharmony_ci#define RECV_WAIT_PACKET_TYPE 0 9962306a36Sopenharmony_ci#define RECV_WAIT_EVENT_HEADER 1 10062306a36Sopenharmony_ci#define RECV_WAIT_ACL_HEADER 2 10162306a36Sopenharmony_ci#define RECV_WAIT_SCO_HEADER 3 10262306a36Sopenharmony_ci#define RECV_WAIT_DATA 4 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* ======================== Special I/O functions ======================== */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define DATA_L 0 11062306a36Sopenharmony_ci#define DATA_H 1 11162306a36Sopenharmony_ci#define ADDR_L 2 11262306a36Sopenharmony_ci#define ADDR_H 3 11362306a36Sopenharmony_ci#define CONTROL 4 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic inline void bt3c_address(unsigned int iobase, unsigned short addr) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci outb(addr & 0xff, iobase + ADDR_L); 11962306a36Sopenharmony_ci outb((addr >> 8) & 0xff, iobase + ADDR_H); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline void bt3c_put(unsigned int iobase, unsigned short value) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci outb(value & 0xff, iobase + DATA_L); 12662306a36Sopenharmony_ci outb((value >> 8) & 0xff, iobase + DATA_H); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci bt3c_address(iobase, addr); 13362306a36Sopenharmony_ci bt3c_put(iobase, value); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic inline unsigned short bt3c_get(unsigned int iobase) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci unsigned short value = inb(iobase + DATA_L); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci value |= inb(iobase + DATA_H) << 8; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return value; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci bt3c_address(iobase, addr); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return bt3c_get(iobase); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* ======================== Interrupt handling ======================== */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int actual = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci bt3c_address(iobase, 0x7080); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Fill FIFO with current frame */ 16662306a36Sopenharmony_ci while (actual < len) { 16762306a36Sopenharmony_ci /* Transmit next byte */ 16862306a36Sopenharmony_ci bt3c_put(iobase, buf[actual]); 16962306a36Sopenharmony_ci actual++; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci bt3c_io_write(iobase, 0x7005, actual); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return actual; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void bt3c_write_wakeup(struct bt3c_info *info) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci if (!info) { 18162306a36Sopenharmony_ci BT_ERR("Unknown device"); 18262306a36Sopenharmony_ci return; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) 18662306a36Sopenharmony_ci return; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci do { 18962306a36Sopenharmony_ci unsigned int iobase = info->p_dev->resource[0]->start; 19062306a36Sopenharmony_ci register struct sk_buff *skb; 19162306a36Sopenharmony_ci int len; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (!pcmcia_dev_present(info->p_dev)) 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci skb = skb_dequeue(&(info->txq)); 19762306a36Sopenharmony_ci if (!skb) { 19862306a36Sopenharmony_ci clear_bit(XMIT_SENDING, &(info->tx_state)); 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Send frame */ 20362306a36Sopenharmony_ci len = bt3c_write(iobase, 256, skb->data, skb->len); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (len != skb->len) 20662306a36Sopenharmony_ci BT_ERR("Very strange"); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci kfree_skb(skb); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci info->hdev->stat.byte_tx += len; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci } while (0); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void bt3c_receive(struct bt3c_info *info) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci unsigned int iobase; 21962306a36Sopenharmony_ci int size = 0, avail; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (!info) { 22262306a36Sopenharmony_ci BT_ERR("Unknown device"); 22362306a36Sopenharmony_ci return; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci iobase = info->p_dev->resource[0]->start; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci avail = bt3c_read(iobase, 0x7006); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci bt3c_address(iobase, 0x7480); 23162306a36Sopenharmony_ci while (size < avail) { 23262306a36Sopenharmony_ci size++; 23362306a36Sopenharmony_ci info->hdev->stat.byte_rx++; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Allocate packet */ 23662306a36Sopenharmony_ci if (!info->rx_skb) { 23762306a36Sopenharmony_ci info->rx_state = RECV_WAIT_PACKET_TYPE; 23862306a36Sopenharmony_ci info->rx_count = 0; 23962306a36Sopenharmony_ci info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); 24062306a36Sopenharmony_ci if (!info->rx_skb) { 24162306a36Sopenharmony_ci BT_ERR("Can't allocate mem for new packet"); 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (info->rx_state == RECV_WAIT_PACKET_TYPE) { 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L); 25062306a36Sopenharmony_ci inb(iobase + DATA_H); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci switch (hci_skb_pkt_type(info->rx_skb)) { 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci case HCI_EVENT_PKT: 25562306a36Sopenharmony_ci info->rx_state = RECV_WAIT_EVENT_HEADER; 25662306a36Sopenharmony_ci info->rx_count = HCI_EVENT_HDR_SIZE; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci case HCI_ACLDATA_PKT: 26062306a36Sopenharmony_ci info->rx_state = RECV_WAIT_ACL_HEADER; 26162306a36Sopenharmony_ci info->rx_count = HCI_ACL_HDR_SIZE; 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci case HCI_SCODATA_PKT: 26562306a36Sopenharmony_ci info->rx_state = RECV_WAIT_SCO_HEADER; 26662306a36Sopenharmony_ci info->rx_count = HCI_SCO_HDR_SIZE; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci default: 27062306a36Sopenharmony_ci /* Unknown packet */ 27162306a36Sopenharmony_ci BT_ERR("Unknown HCI packet with type 0x%02x received", 27262306a36Sopenharmony_ci hci_skb_pkt_type(info->rx_skb)); 27362306a36Sopenharmony_ci info->hdev->stat.err_rx++; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci kfree_skb(info->rx_skb); 27662306a36Sopenharmony_ci info->rx_skb = NULL; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci __u8 x = inb(iobase + DATA_L); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci skb_put_u8(info->rx_skb, x); 28662306a36Sopenharmony_ci inb(iobase + DATA_H); 28762306a36Sopenharmony_ci info->rx_count--; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (info->rx_count == 0) { 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci int dlen; 29262306a36Sopenharmony_ci struct hci_event_hdr *eh; 29362306a36Sopenharmony_ci struct hci_acl_hdr *ah; 29462306a36Sopenharmony_ci struct hci_sco_hdr *sh; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci switch (info->rx_state) { 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci case RECV_WAIT_EVENT_HEADER: 29962306a36Sopenharmony_ci eh = hci_event_hdr(info->rx_skb); 30062306a36Sopenharmony_ci info->rx_state = RECV_WAIT_DATA; 30162306a36Sopenharmony_ci info->rx_count = eh->plen; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci case RECV_WAIT_ACL_HEADER: 30562306a36Sopenharmony_ci ah = hci_acl_hdr(info->rx_skb); 30662306a36Sopenharmony_ci dlen = __le16_to_cpu(ah->dlen); 30762306a36Sopenharmony_ci info->rx_state = RECV_WAIT_DATA; 30862306a36Sopenharmony_ci info->rx_count = dlen; 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci case RECV_WAIT_SCO_HEADER: 31262306a36Sopenharmony_ci sh = hci_sco_hdr(info->rx_skb); 31362306a36Sopenharmony_ci info->rx_state = RECV_WAIT_DATA; 31462306a36Sopenharmony_ci info->rx_count = sh->dlen; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci case RECV_WAIT_DATA: 31862306a36Sopenharmony_ci hci_recv_frame(info->hdev, info->rx_skb); 31962306a36Sopenharmony_ci info->rx_skb = NULL; 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci bt3c_io_write(iobase, 0x7006, 0x0000); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic irqreturn_t bt3c_interrupt(int irq, void *dev_inst) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct bt3c_info *info = dev_inst; 33762306a36Sopenharmony_ci unsigned int iobase; 33862306a36Sopenharmony_ci int iir; 33962306a36Sopenharmony_ci irqreturn_t r = IRQ_NONE; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!info || !info->hdev) 34262306a36Sopenharmony_ci /* our irq handler is shared */ 34362306a36Sopenharmony_ci return IRQ_NONE; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci iobase = info->p_dev->resource[0]->start; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci spin_lock(&(info->lock)); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci iir = inb(iobase + CONTROL); 35062306a36Sopenharmony_ci if (iir & 0x80) { 35162306a36Sopenharmony_ci int stat = bt3c_read(iobase, 0x7001); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if ((stat & 0xff) == 0x7f) { 35462306a36Sopenharmony_ci BT_ERR("Very strange (stat=0x%04x)", stat); 35562306a36Sopenharmony_ci } else if ((stat & 0xff) != 0xff) { 35662306a36Sopenharmony_ci if (stat & 0x0020) { 35762306a36Sopenharmony_ci int status = bt3c_read(iobase, 0x7002) & 0x10; 35862306a36Sopenharmony_ci bt_dev_info(info->hdev, "Antenna %s", 35962306a36Sopenharmony_ci status ? "out" : "in"); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci if (stat & 0x0001) 36262306a36Sopenharmony_ci bt3c_receive(info); 36362306a36Sopenharmony_ci if (stat & 0x0002) { 36462306a36Sopenharmony_ci clear_bit(XMIT_SENDING, &(info->tx_state)); 36562306a36Sopenharmony_ci bt3c_write_wakeup(info); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci bt3c_io_write(iobase, 0x7001, 0x0000); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci outb(iir, iobase + CONTROL); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci r = IRQ_HANDLED; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci spin_unlock(&(info->lock)); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return r; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* ======================== HCI interface ======================== */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int bt3c_hci_flush(struct hci_dev *hdev) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct bt3c_info *info = hci_get_drvdata(hdev); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Drop TX queue */ 39062306a36Sopenharmony_ci skb_queue_purge(&(info->txq)); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int bt3c_hci_open(struct hci_dev *hdev) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int bt3c_hci_close(struct hci_dev *hdev) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci bt3c_hci_flush(hdev); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct bt3c_info *info = hci_get_drvdata(hdev); 41362306a36Sopenharmony_ci unsigned long flags; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci switch (hci_skb_pkt_type(skb)) { 41662306a36Sopenharmony_ci case HCI_COMMAND_PKT: 41762306a36Sopenharmony_ci hdev->stat.cmd_tx++; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci case HCI_ACLDATA_PKT: 42062306a36Sopenharmony_ci hdev->stat.acl_tx++; 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci case HCI_SCODATA_PKT: 42362306a36Sopenharmony_ci hdev->stat.sco_tx++; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Prepend skb with frame type */ 42862306a36Sopenharmony_ci memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); 42962306a36Sopenharmony_ci skb_queue_tail(&(info->txq), skb); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci spin_lock_irqsave(&(info->lock), flags); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci bt3c_write_wakeup(info); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_unlock_irqrestore(&(info->lock), flags); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci/* ======================== Card services HCI interaction ======================== */ 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic int bt3c_load_firmware(struct bt3c_info *info, 44662306a36Sopenharmony_ci const unsigned char *firmware, 44762306a36Sopenharmony_ci int count) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci char *ptr = (char *) firmware; 45062306a36Sopenharmony_ci char b[9]; 45162306a36Sopenharmony_ci unsigned int iobase, tmp, tn; 45262306a36Sopenharmony_ci unsigned long size, addr, fcs; 45362306a36Sopenharmony_ci int i, err = 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci iobase = info->p_dev->resource[0]->start; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Reset */ 45862306a36Sopenharmony_ci bt3c_io_write(iobase, 0x8040, 0x0404); 45962306a36Sopenharmony_ci bt3c_io_write(iobase, 0x8040, 0x0400); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci udelay(1); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci bt3c_io_write(iobase, 0x8040, 0x0404); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci udelay(17); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Load */ 46862306a36Sopenharmony_ci while (count) { 46962306a36Sopenharmony_ci if (ptr[0] != 'S') { 47062306a36Sopenharmony_ci BT_ERR("Bad address in firmware"); 47162306a36Sopenharmony_ci err = -EFAULT; 47262306a36Sopenharmony_ci goto error; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci memset(b, 0, sizeof(b)); 47662306a36Sopenharmony_ci memcpy(b, ptr + 2, 2); 47762306a36Sopenharmony_ci if (kstrtoul(b, 16, &size) < 0) 47862306a36Sopenharmony_ci return -EINVAL; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci memset(b, 0, sizeof(b)); 48162306a36Sopenharmony_ci memcpy(b, ptr + 4, 8); 48262306a36Sopenharmony_ci if (kstrtoul(b, 16, &addr) < 0) 48362306a36Sopenharmony_ci return -EINVAL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci memset(b, 0, sizeof(b)); 48662306a36Sopenharmony_ci memcpy(b, ptr + (size * 2) + 2, 2); 48762306a36Sopenharmony_ci if (kstrtoul(b, 16, &fcs) < 0) 48862306a36Sopenharmony_ci return -EINVAL; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci memset(b, 0, sizeof(b)); 49162306a36Sopenharmony_ci for (tmp = 0, i = 0; i < size; i++) { 49262306a36Sopenharmony_ci memcpy(b, ptr + (i * 2) + 2, 2); 49362306a36Sopenharmony_ci if (kstrtouint(b, 16, &tn)) 49462306a36Sopenharmony_ci return -EINVAL; 49562306a36Sopenharmony_ci tmp += tn; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (((tmp + fcs) & 0xff) != 0xff) { 49962306a36Sopenharmony_ci BT_ERR("Checksum error in firmware"); 50062306a36Sopenharmony_ci err = -EILSEQ; 50162306a36Sopenharmony_ci goto error; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (ptr[1] == '3') { 50562306a36Sopenharmony_ci bt3c_address(iobase, addr); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci memset(b, 0, sizeof(b)); 50862306a36Sopenharmony_ci for (i = 0; i < (size - 4) / 2; i++) { 50962306a36Sopenharmony_ci memcpy(b, ptr + (i * 4) + 12, 4); 51062306a36Sopenharmony_ci if (kstrtouint(b, 16, &tmp)) 51162306a36Sopenharmony_ci return -EINVAL; 51262306a36Sopenharmony_ci bt3c_put(iobase, tmp); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ptr += (size * 2) + 6; 51762306a36Sopenharmony_ci count -= (size * 2) + 6; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci udelay(17); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Boot */ 52362306a36Sopenharmony_ci bt3c_address(iobase, 0x3000); 52462306a36Sopenharmony_ci outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cierror: 52762306a36Sopenharmony_ci udelay(17); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Clear */ 53062306a36Sopenharmony_ci bt3c_io_write(iobase, 0x7006, 0x0000); 53162306a36Sopenharmony_ci bt3c_io_write(iobase, 0x7005, 0x0000); 53262306a36Sopenharmony_ci bt3c_io_write(iobase, 0x7001, 0x0000); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return err; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int bt3c_open(struct bt3c_info *info) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci const struct firmware *firmware; 54162306a36Sopenharmony_ci struct hci_dev *hdev; 54262306a36Sopenharmony_ci int err; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci spin_lock_init(&(info->lock)); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci skb_queue_head_init(&(info->txq)); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci info->rx_state = RECV_WAIT_PACKET_TYPE; 54962306a36Sopenharmony_ci info->rx_count = 0; 55062306a36Sopenharmony_ci info->rx_skb = NULL; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Initialize HCI device */ 55362306a36Sopenharmony_ci hdev = hci_alloc_dev(); 55462306a36Sopenharmony_ci if (!hdev) { 55562306a36Sopenharmony_ci BT_ERR("Can't allocate HCI device"); 55662306a36Sopenharmony_ci return -ENOMEM; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci info->hdev = hdev; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci hdev->bus = HCI_PCCARD; 56262306a36Sopenharmony_ci hci_set_drvdata(hdev, info); 56362306a36Sopenharmony_ci SET_HCIDEV_DEV(hdev, &info->p_dev->dev); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci hdev->open = bt3c_hci_open; 56662306a36Sopenharmony_ci hdev->close = bt3c_hci_close; 56762306a36Sopenharmony_ci hdev->flush = bt3c_hci_flush; 56862306a36Sopenharmony_ci hdev->send = bt3c_hci_send_frame; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Load firmware */ 57162306a36Sopenharmony_ci err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev); 57262306a36Sopenharmony_ci if (err < 0) { 57362306a36Sopenharmony_ci BT_ERR("Firmware request failed"); 57462306a36Sopenharmony_ci goto error; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci err = bt3c_load_firmware(info, firmware->data, firmware->size); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci release_firmware(firmware); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (err < 0) { 58262306a36Sopenharmony_ci BT_ERR("Firmware loading failed"); 58362306a36Sopenharmony_ci goto error; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Timeout before it is safe to send the first HCI packet */ 58762306a36Sopenharmony_ci msleep(1000); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Register HCI device */ 59062306a36Sopenharmony_ci err = hci_register_dev(hdev); 59162306a36Sopenharmony_ci if (err < 0) { 59262306a36Sopenharmony_ci BT_ERR("Can't register HCI device"); 59362306a36Sopenharmony_ci goto error; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cierror: 59962306a36Sopenharmony_ci info->hdev = NULL; 60062306a36Sopenharmony_ci hci_free_dev(hdev); 60162306a36Sopenharmony_ci return err; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int bt3c_close(struct bt3c_info *info) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct hci_dev *hdev = info->hdev; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!hdev) 61062306a36Sopenharmony_ci return -ENODEV; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci bt3c_hci_close(hdev); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci hci_unregister_dev(hdev); 61562306a36Sopenharmony_ci hci_free_dev(hdev); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int bt3c_probe(struct pcmcia_device *link) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct bt3c_info *info; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Create new info device */ 62562306a36Sopenharmony_ci info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); 62662306a36Sopenharmony_ci if (!info) 62762306a36Sopenharmony_ci return -ENOMEM; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci info->p_dev = link; 63062306a36Sopenharmony_ci link->priv = info; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | 63362306a36Sopenharmony_ci CONF_AUTO_SET_IO; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return bt3c_config(link); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic void bt3c_detach(struct pcmcia_device *link) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci bt3c_release(link); 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci int *try = priv_data; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (!try) 64962306a36Sopenharmony_ci p_dev->io_lines = 16; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) 65262306a36Sopenharmony_ci return -EINVAL; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci p_dev->resource[0]->end = 8; 65562306a36Sopenharmony_ci p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; 65662306a36Sopenharmony_ci p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return pcmcia_request_io(p_dev); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int bt3c_check_config_notpicky(struct pcmcia_device *p_dev, 66262306a36Sopenharmony_ci void *priv_data) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; 66562306a36Sopenharmony_ci int j; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (p_dev->io_lines > 3) 66862306a36Sopenharmony_ci return -ENODEV; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; 67162306a36Sopenharmony_ci p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; 67262306a36Sopenharmony_ci p_dev->resource[0]->end = 8; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci for (j = 0; j < 5; j++) { 67562306a36Sopenharmony_ci p_dev->resource[0]->start = base[j]; 67662306a36Sopenharmony_ci p_dev->io_lines = base[j] ? 16 : 3; 67762306a36Sopenharmony_ci if (!pcmcia_request_io(p_dev)) 67862306a36Sopenharmony_ci return 0; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci return -ENODEV; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int bt3c_config(struct pcmcia_device *link) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct bt3c_info *info = link->priv; 68662306a36Sopenharmony_ci int i; 68762306a36Sopenharmony_ci unsigned long try; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* First pass: look for a config entry that looks normal. 69062306a36Sopenharmony_ci * Two tries: without IO aliases, then with aliases 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci for (try = 0; try < 2; try++) 69362306a36Sopenharmony_ci if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try)) 69462306a36Sopenharmony_ci goto found_port; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Second pass: try to find an entry that isn't picky about 69762306a36Sopenharmony_ci * its base address, then try to grab any standard serial port 69862306a36Sopenharmony_ci * address, and finally try to get any free port. 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_ci if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL)) 70162306a36Sopenharmony_ci goto found_port; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci BT_ERR("No usable port range found"); 70462306a36Sopenharmony_ci goto failed; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cifound_port: 70762306a36Sopenharmony_ci i = pcmcia_request_irq(link, &bt3c_interrupt); 70862306a36Sopenharmony_ci if (i != 0) 70962306a36Sopenharmony_ci goto failed; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci i = pcmcia_enable_device(link); 71262306a36Sopenharmony_ci if (i != 0) 71362306a36Sopenharmony_ci goto failed; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (bt3c_open(info) != 0) 71662306a36Sopenharmony_ci goto failed; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cifailed: 72162306a36Sopenharmony_ci bt3c_release(link); 72262306a36Sopenharmony_ci return -ENODEV; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void bt3c_release(struct pcmcia_device *link) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct bt3c_info *info = link->priv; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci bt3c_close(info); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci pcmcia_disable_device(link); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic const struct pcmcia_device_id bt3c_ids[] = { 73762306a36Sopenharmony_ci PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02), 73862306a36Sopenharmony_ci PCMCIA_DEVICE_NULL 73962306a36Sopenharmony_ci}; 74062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pcmcia, bt3c_ids); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic struct pcmcia_driver bt3c_driver = { 74362306a36Sopenharmony_ci .owner = THIS_MODULE, 74462306a36Sopenharmony_ci .name = "bt3c_cs", 74562306a36Sopenharmony_ci .probe = bt3c_probe, 74662306a36Sopenharmony_ci .remove = bt3c_detach, 74762306a36Sopenharmony_ci .id_table = bt3c_ids, 74862306a36Sopenharmony_ci}; 74962306a36Sopenharmony_cimodule_pcmcia_driver(bt3c_driver); 750