162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci *  A driver for Nokia Connectivity Card DTL-1 devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  This program is free software; you can redistribute it and/or modify
962306a36Sopenharmony_ci *  it under the terms of the GNU General Public License version 2 as
1062306a36Sopenharmony_ci *  published by the Free Software Foundation;
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  Software distributed under the License is distributed on an "AS
1362306a36Sopenharmony_ci *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
1462306a36Sopenharmony_ci *  implied. See the License for the specific language governing
1562306a36Sopenharmony_ci *  rights and limitations under the License.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *  The initial developer of the original code is David A. Hinds
1862306a36Sopenharmony_ci *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
1962306a36Sopenharmony_ci *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/kernel.h>
2662306a36Sopenharmony_ci#include <linux/init.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/types.h>
2962306a36Sopenharmony_ci#include <linux/delay.h>
3062306a36Sopenharmony_ci#include <linux/errno.h>
3162306a36Sopenharmony_ci#include <linux/ptrace.h>
3262306a36Sopenharmony_ci#include <linux/ioport.h>
3362306a36Sopenharmony_ci#include <linux/spinlock.h>
3462306a36Sopenharmony_ci#include <linux/moduleparam.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include <linux/skbuff.h>
3762306a36Sopenharmony_ci#include <linux/string.h>
3862306a36Sopenharmony_ci#include <linux/serial.h>
3962306a36Sopenharmony_ci#include <linux/serial_reg.h>
4062306a36Sopenharmony_ci#include <linux/bitops.h>
4162306a36Sopenharmony_ci#include <asm/io.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
4462306a36Sopenharmony_ci#include <pcmcia/ciscode.h>
4562306a36Sopenharmony_ci#include <pcmcia/ds.h>
4662306a36Sopenharmony_ci#include <pcmcia/cisreg.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h>
4962306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* ======================== Module parameters ======================== */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciMODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
5762306a36Sopenharmony_ciMODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1");
5862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* ======================== Local structures ======================== */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct dtl1_info {
6662306a36Sopenharmony_ci	struct pcmcia_device *p_dev;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	struct hci_dev *hdev;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	spinlock_t lock;		/* For serializing operations */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	unsigned long flowmask;		/* HCI flow mask */
7362306a36Sopenharmony_ci	int ri_latch;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	struct sk_buff_head txq;
7662306a36Sopenharmony_ci	unsigned long tx_state;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	unsigned long rx_state;
7962306a36Sopenharmony_ci	unsigned long rx_count;
8062306a36Sopenharmony_ci	struct sk_buff *rx_skb;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int dtl1_config(struct pcmcia_device *link);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Transmit states  */
8862306a36Sopenharmony_ci#define XMIT_SENDING  1
8962306a36Sopenharmony_ci#define XMIT_WAKEUP   2
9062306a36Sopenharmony_ci#define XMIT_WAITING  8
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Receiver States */
9362306a36Sopenharmony_ci#define RECV_WAIT_NSH   0
9462306a36Sopenharmony_ci#define RECV_WAIT_DATA  1
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct nsh {
9862306a36Sopenharmony_ci	u8 type;
9962306a36Sopenharmony_ci	u8 zero;
10062306a36Sopenharmony_ci	u16 len;
10162306a36Sopenharmony_ci} __packed;	/* Nokia Specific Header */
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define NSHL  4				/* Nokia Specific Header Length */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/* ======================== Interrupt handling ======================== */
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	int actual = 0;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Tx FIFO should be empty */
11562306a36Sopenharmony_ci	if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
11662306a36Sopenharmony_ci		return 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Fill FIFO with current frame */
11962306a36Sopenharmony_ci	while ((fifo_size-- > 0) && (actual < len)) {
12062306a36Sopenharmony_ci		/* Transmit next byte */
12162306a36Sopenharmony_ci		outb(buf[actual], iobase + UART_TX);
12262306a36Sopenharmony_ci		actual++;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return actual;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void dtl1_write_wakeup(struct dtl1_info *info)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	if (!info) {
13262306a36Sopenharmony_ci		BT_ERR("Unknown device");
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (test_bit(XMIT_WAITING, &(info->tx_state))) {
13762306a36Sopenharmony_ci		set_bit(XMIT_WAKEUP, &(info->tx_state));
13862306a36Sopenharmony_ci		return;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
14262306a36Sopenharmony_ci		set_bit(XMIT_WAKEUP, &(info->tx_state));
14362306a36Sopenharmony_ci		return;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	do {
14762306a36Sopenharmony_ci		unsigned int iobase = info->p_dev->resource[0]->start;
14862306a36Sopenharmony_ci		register struct sk_buff *skb;
14962306a36Sopenharmony_ci		int len;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		clear_bit(XMIT_WAKEUP, &(info->tx_state));
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		if (!pcmcia_dev_present(info->p_dev))
15462306a36Sopenharmony_ci			return;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		skb = skb_dequeue(&(info->txq));
15762306a36Sopenharmony_ci		if (!skb)
15862306a36Sopenharmony_ci			break;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		/* Send frame */
16162306a36Sopenharmony_ci		len = dtl1_write(iobase, 32, skb->data, skb->len);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		if (len == skb->len) {
16462306a36Sopenharmony_ci			set_bit(XMIT_WAITING, &(info->tx_state));
16562306a36Sopenharmony_ci			kfree_skb(skb);
16662306a36Sopenharmony_ci		} else {
16762306a36Sopenharmony_ci			skb_pull(skb, len);
16862306a36Sopenharmony_ci			skb_queue_head(&(info->txq), skb);
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		info->hdev->stat.byte_tx += len;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	clear_bit(XMIT_SENDING, &(info->tx_state));
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void dtl1_control(struct dtl1_info *info, struct sk_buff *skb)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	u8 flowmask = *(u8 *)skb->data;
18262306a36Sopenharmony_ci	int i;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	printk(KERN_INFO "Bluetooth: Nokia control data =");
18562306a36Sopenharmony_ci	for (i = 0; i < skb->len; i++)
18662306a36Sopenharmony_ci		printk(" %02x", skb->data[i]);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	printk("\n");
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* transition to active state */
19162306a36Sopenharmony_ci	if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
19262306a36Sopenharmony_ci		clear_bit(XMIT_WAITING, &(info->tx_state));
19362306a36Sopenharmony_ci		dtl1_write_wakeup(info);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	info->flowmask = flowmask;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	kfree_skb(skb);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void dtl1_receive(struct dtl1_info *info)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	unsigned int iobase;
20562306a36Sopenharmony_ci	struct nsh *nsh;
20662306a36Sopenharmony_ci	int boguscount = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!info) {
20962306a36Sopenharmony_ci		BT_ERR("Unknown device");
21062306a36Sopenharmony_ci		return;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	iobase = info->p_dev->resource[0]->start;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	do {
21662306a36Sopenharmony_ci		info->hdev->stat.byte_rx++;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		/* Allocate packet */
21962306a36Sopenharmony_ci		if (info->rx_skb == NULL) {
22062306a36Sopenharmony_ci			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
22162306a36Sopenharmony_ci			if (!info->rx_skb) {
22262306a36Sopenharmony_ci				BT_ERR("Can't allocate mem for new packet");
22362306a36Sopenharmony_ci				info->rx_state = RECV_WAIT_NSH;
22462306a36Sopenharmony_ci				info->rx_count = NSHL;
22562306a36Sopenharmony_ci				return;
22662306a36Sopenharmony_ci			}
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		skb_put_u8(info->rx_skb, inb(iobase + UART_RX));
23062306a36Sopenharmony_ci		nsh = (struct nsh *)info->rx_skb->data;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		info->rx_count--;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		if (info->rx_count == 0) {
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci			switch (info->rx_state) {
23762306a36Sopenharmony_ci			case RECV_WAIT_NSH:
23862306a36Sopenharmony_ci				info->rx_state = RECV_WAIT_DATA;
23962306a36Sopenharmony_ci				info->rx_count = nsh->len + (nsh->len & 0x0001);
24062306a36Sopenharmony_ci				break;
24162306a36Sopenharmony_ci			case RECV_WAIT_DATA:
24262306a36Sopenharmony_ci				hci_skb_pkt_type(info->rx_skb) = nsh->type;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci				/* remove PAD byte if it exists */
24562306a36Sopenharmony_ci				if (nsh->len & 0x0001) {
24662306a36Sopenharmony_ci					info->rx_skb->tail--;
24762306a36Sopenharmony_ci					info->rx_skb->len--;
24862306a36Sopenharmony_ci				}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci				/* remove NSH */
25162306a36Sopenharmony_ci				skb_pull(info->rx_skb, NSHL);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci				switch (hci_skb_pkt_type(info->rx_skb)) {
25462306a36Sopenharmony_ci				case 0x80:
25562306a36Sopenharmony_ci					/* control data for the Nokia Card */
25662306a36Sopenharmony_ci					dtl1_control(info, info->rx_skb);
25762306a36Sopenharmony_ci					break;
25862306a36Sopenharmony_ci				case 0x82:
25962306a36Sopenharmony_ci				case 0x83:
26062306a36Sopenharmony_ci				case 0x84:
26162306a36Sopenharmony_ci					/* send frame to the HCI layer */
26262306a36Sopenharmony_ci					hci_skb_pkt_type(info->rx_skb) &= 0x0f;
26362306a36Sopenharmony_ci					hci_recv_frame(info->hdev, info->rx_skb);
26462306a36Sopenharmony_ci					break;
26562306a36Sopenharmony_ci				default:
26662306a36Sopenharmony_ci					/* unknown packet */
26762306a36Sopenharmony_ci					BT_ERR("Unknown HCI packet with type 0x%02x received",
26862306a36Sopenharmony_ci					       hci_skb_pkt_type(info->rx_skb));
26962306a36Sopenharmony_ci					kfree_skb(info->rx_skb);
27062306a36Sopenharmony_ci					break;
27162306a36Sopenharmony_ci				}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci				info->rx_state = RECV_WAIT_NSH;
27462306a36Sopenharmony_ci				info->rx_count = NSHL;
27562306a36Sopenharmony_ci				info->rx_skb = NULL;
27662306a36Sopenharmony_ci				break;
27762306a36Sopenharmony_ci			}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		/* Make sure we don't stay here too long */
28262306a36Sopenharmony_ci		if (boguscount++ > 32)
28362306a36Sopenharmony_ci			break;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	} while (inb(iobase + UART_LSR) & UART_LSR_DR);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct dtl1_info *info = dev_inst;
29262306a36Sopenharmony_ci	unsigned int iobase;
29362306a36Sopenharmony_ci	unsigned char msr;
29462306a36Sopenharmony_ci	int boguscount = 0;
29562306a36Sopenharmony_ci	int iir, lsr;
29662306a36Sopenharmony_ci	irqreturn_t r = IRQ_NONE;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (!info || !info->hdev)
29962306a36Sopenharmony_ci		/* our irq handler is shared */
30062306a36Sopenharmony_ci		return IRQ_NONE;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	iobase = info->p_dev->resource[0]->start;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	spin_lock(&(info->lock));
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	iir = inb(iobase + UART_IIR) & UART_IIR_ID;
30762306a36Sopenharmony_ci	while (iir) {
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		r = IRQ_HANDLED;
31062306a36Sopenharmony_ci		/* Clear interrupt */
31162306a36Sopenharmony_ci		lsr = inb(iobase + UART_LSR);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		switch (iir) {
31462306a36Sopenharmony_ci		case UART_IIR_RLSI:
31562306a36Sopenharmony_ci			BT_ERR("RLSI");
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci		case UART_IIR_RDI:
31862306a36Sopenharmony_ci			/* Receive interrupt */
31962306a36Sopenharmony_ci			dtl1_receive(info);
32062306a36Sopenharmony_ci			break;
32162306a36Sopenharmony_ci		case UART_IIR_THRI:
32262306a36Sopenharmony_ci			if (lsr & UART_LSR_THRE) {
32362306a36Sopenharmony_ci				/* Transmitter ready for data */
32462306a36Sopenharmony_ci				dtl1_write_wakeup(info);
32562306a36Sopenharmony_ci			}
32662306a36Sopenharmony_ci			break;
32762306a36Sopenharmony_ci		default:
32862306a36Sopenharmony_ci			BT_ERR("Unhandled IIR=%#x", iir);
32962306a36Sopenharmony_ci			break;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		/* Make sure we don't stay here too long */
33362306a36Sopenharmony_ci		if (boguscount++ > 100)
33462306a36Sopenharmony_ci			break;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		iir = inb(iobase + UART_IIR) & UART_IIR_ID;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	msr = inb(iobase + UART_MSR);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (info->ri_latch ^ (msr & UART_MSR_RI)) {
34362306a36Sopenharmony_ci		info->ri_latch = msr & UART_MSR_RI;
34462306a36Sopenharmony_ci		clear_bit(XMIT_WAITING, &(info->tx_state));
34562306a36Sopenharmony_ci		dtl1_write_wakeup(info);
34662306a36Sopenharmony_ci		r = IRQ_HANDLED;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	spin_unlock(&(info->lock));
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return r;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/* ======================== HCI interface ======================== */
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int dtl1_hci_open(struct hci_dev *hdev)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int dtl1_hci_flush(struct hci_dev *hdev)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct dtl1_info *info = hci_get_drvdata(hdev);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Drop TX queue */
37062306a36Sopenharmony_ci	skb_queue_purge(&(info->txq));
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return 0;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int dtl1_hci_close(struct hci_dev *hdev)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	dtl1_hci_flush(hdev);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct dtl1_info *info = hci_get_drvdata(hdev);
38762306a36Sopenharmony_ci	struct sk_buff *s;
38862306a36Sopenharmony_ci	struct nsh nsh;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	switch (hci_skb_pkt_type(skb)) {
39162306a36Sopenharmony_ci	case HCI_COMMAND_PKT:
39262306a36Sopenharmony_ci		hdev->stat.cmd_tx++;
39362306a36Sopenharmony_ci		nsh.type = 0x81;
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	case HCI_ACLDATA_PKT:
39662306a36Sopenharmony_ci		hdev->stat.acl_tx++;
39762306a36Sopenharmony_ci		nsh.type = 0x82;
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci	case HCI_SCODATA_PKT:
40062306a36Sopenharmony_ci		hdev->stat.sco_tx++;
40162306a36Sopenharmony_ci		nsh.type = 0x83;
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci	default:
40462306a36Sopenharmony_ci		return -EILSEQ;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	nsh.zero = 0;
40862306a36Sopenharmony_ci	nsh.len = skb->len;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
41162306a36Sopenharmony_ci	if (!s)
41262306a36Sopenharmony_ci		return -ENOMEM;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	skb_reserve(s, NSHL);
41562306a36Sopenharmony_ci	skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
41662306a36Sopenharmony_ci	if (skb->len & 0x0001)
41762306a36Sopenharmony_ci		skb_put_u8(s, 0);	/* PAD */
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* Prepend skb with Nokia frame header and queue */
42062306a36Sopenharmony_ci	memcpy(skb_push(s, NSHL), &nsh, NSHL);
42162306a36Sopenharmony_ci	skb_queue_tail(&(info->txq), s);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	dtl1_write_wakeup(info);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	kfree_skb(skb);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return 0;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/* ======================== Card services HCI interaction ======================== */
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int dtl1_open(struct dtl1_info *info)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	unsigned long flags;
43862306a36Sopenharmony_ci	unsigned int iobase = info->p_dev->resource[0]->start;
43962306a36Sopenharmony_ci	struct hci_dev *hdev;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	spin_lock_init(&(info->lock));
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	skb_queue_head_init(&(info->txq));
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	info->rx_state = RECV_WAIT_NSH;
44662306a36Sopenharmony_ci	info->rx_count = NSHL;
44762306a36Sopenharmony_ci	info->rx_skb = NULL;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	set_bit(XMIT_WAITING, &(info->tx_state));
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* Initialize HCI device */
45262306a36Sopenharmony_ci	hdev = hci_alloc_dev();
45362306a36Sopenharmony_ci	if (!hdev) {
45462306a36Sopenharmony_ci		BT_ERR("Can't allocate HCI device");
45562306a36Sopenharmony_ci		return -ENOMEM;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	info->hdev = hdev;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	hdev->bus = HCI_PCCARD;
46162306a36Sopenharmony_ci	hci_set_drvdata(hdev, info);
46262306a36Sopenharmony_ci	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	hdev->open  = dtl1_hci_open;
46562306a36Sopenharmony_ci	hdev->close = dtl1_hci_close;
46662306a36Sopenharmony_ci	hdev->flush = dtl1_hci_flush;
46762306a36Sopenharmony_ci	hdev->send  = dtl1_hci_send_frame;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	spin_lock_irqsave(&(info->lock), flags);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* Reset UART */
47262306a36Sopenharmony_ci	outb(0, iobase + UART_MCR);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* Turn off interrupts */
47562306a36Sopenharmony_ci	outb(0, iobase + UART_IER);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* Initialize UART */
47862306a36Sopenharmony_ci	outb(UART_LCR_WLEN8, iobase + UART_LCR);	/* Reset DLAB */
47962306a36Sopenharmony_ci	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR)
48262306a36Sopenharmony_ci				& UART_MSR_RI;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* Turn on interrupts */
48562306a36Sopenharmony_ci	outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	spin_unlock_irqrestore(&(info->lock), flags);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* Timeout before it is safe to send the first HCI packet */
49062306a36Sopenharmony_ci	msleep(2000);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* Register HCI device */
49362306a36Sopenharmony_ci	if (hci_register_dev(hdev) < 0) {
49462306a36Sopenharmony_ci		BT_ERR("Can't register HCI device");
49562306a36Sopenharmony_ci		info->hdev = NULL;
49662306a36Sopenharmony_ci		hci_free_dev(hdev);
49762306a36Sopenharmony_ci		return -ENODEV;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int dtl1_close(struct dtl1_info *info)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	unsigned long flags;
50762306a36Sopenharmony_ci	unsigned int iobase = info->p_dev->resource[0]->start;
50862306a36Sopenharmony_ci	struct hci_dev *hdev = info->hdev;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (!hdev)
51162306a36Sopenharmony_ci		return -ENODEV;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	dtl1_hci_close(hdev);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	spin_lock_irqsave(&(info->lock), flags);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Reset UART */
51862306a36Sopenharmony_ci	outb(0, iobase + UART_MCR);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* Turn off interrupts */
52162306a36Sopenharmony_ci	outb(0, iobase + UART_IER);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	spin_unlock_irqrestore(&(info->lock), flags);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	hci_unregister_dev(hdev);
52662306a36Sopenharmony_ci	hci_free_dev(hdev);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int dtl1_probe(struct pcmcia_device *link)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct dtl1_info *info;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Create new info device */
53662306a36Sopenharmony_ci	info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
53762306a36Sopenharmony_ci	if (!info)
53862306a36Sopenharmony_ci		return -ENOMEM;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	info->p_dev = link;
54162306a36Sopenharmony_ci	link->priv = info;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return dtl1_config(link);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void dtl1_detach(struct pcmcia_device *link)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct dtl1_info *info = link->priv;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	dtl1_close(info);
55462306a36Sopenharmony_ci	pcmcia_disable_device(link);
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8))
56062306a36Sopenharmony_ci		return -ENODEV;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
56362306a36Sopenharmony_ci	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return pcmcia_request_io(p_dev);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int dtl1_config(struct pcmcia_device *link)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct dtl1_info *info = link->priv;
57162306a36Sopenharmony_ci	int ret;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* Look for a generic full-sized window */
57462306a36Sopenharmony_ci	link->resource[0]->end = 8;
57562306a36Sopenharmony_ci	ret = pcmcia_loop_config(link, dtl1_confcheck, NULL);
57662306a36Sopenharmony_ci	if (ret)
57762306a36Sopenharmony_ci		goto failed;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ret = pcmcia_request_irq(link, dtl1_interrupt);
58062306a36Sopenharmony_ci	if (ret)
58162306a36Sopenharmony_ci		goto failed;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	ret = pcmcia_enable_device(link);
58462306a36Sopenharmony_ci	if (ret)
58562306a36Sopenharmony_ci		goto failed;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ret = dtl1_open(info);
58862306a36Sopenharmony_ci	if (ret)
58962306a36Sopenharmony_ci		goto failed;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cifailed:
59462306a36Sopenharmony_ci	dtl1_detach(link);
59562306a36Sopenharmony_ci	return ret;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic const struct pcmcia_device_id dtl1_ids[] = {
59962306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
60062306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
60162306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
60262306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3),
60362306a36Sopenharmony_ci	PCMCIA_DEVICE_NULL
60462306a36Sopenharmony_ci};
60562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pcmcia, dtl1_ids);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic struct pcmcia_driver dtl1_driver = {
60862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
60962306a36Sopenharmony_ci	.name		= "dtl1_cs",
61062306a36Sopenharmony_ci	.probe		= dtl1_probe,
61162306a36Sopenharmony_ci	.remove		= dtl1_detach,
61262306a36Sopenharmony_ci	.id_table	= dtl1_ids,
61362306a36Sopenharmony_ci};
61462306a36Sopenharmony_cimodule_pcmcia_driver(dtl1_driver);
615