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