xref: /openbmc/linux/drivers/bluetooth/bt3c_cs.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  *  Driver for the 3Com Bluetooth PCMCIA card
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
61da177e4SLinus Torvalds  *                           Jose Orlando Pereira <jop@di.uminho.pt>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  This program is free software; you can redistribute it and/or modify
101da177e4SLinus Torvalds  *  it under the terms of the GNU General Public License version 2 as
111da177e4SLinus Torvalds  *  published by the Free Software Foundation;
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  *  Software distributed under the License is distributed on an "AS
141da177e4SLinus Torvalds  *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
151da177e4SLinus Torvalds  *  implied. See the License for the specific language governing
161da177e4SLinus Torvalds  *  rights and limitations under the License.
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *  The initial developer of the original code is David A. Hinds
191da177e4SLinus Torvalds  *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
201da177e4SLinus Torvalds  *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #include <linux/module.h>
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <linux/kernel.h>
271da177e4SLinus Torvalds #include <linux/init.h>
281da177e4SLinus Torvalds #include <linux/slab.h>
291da177e4SLinus Torvalds #include <linux/types.h>
301da177e4SLinus Torvalds #include <linux/delay.h>
311da177e4SLinus Torvalds #include <linux/errno.h>
321da177e4SLinus Torvalds #include <linux/ptrace.h>
331da177e4SLinus Torvalds #include <linux/ioport.h>
341da177e4SLinus Torvalds #include <linux/spinlock.h>
351da177e4SLinus Torvalds #include <linux/moduleparam.h>
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #include <linux/skbuff.h>
381da177e4SLinus Torvalds #include <linux/string.h>
391da177e4SLinus Torvalds #include <linux/serial.h>
401da177e4SLinus Torvalds #include <linux/serial_reg.h>
411da177e4SLinus Torvalds #include <linux/bitops.h>
421da177e4SLinus Torvalds #include <asm/io.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #include <linux/device.h>
451da177e4SLinus Torvalds #include <linux/firmware.h>
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #include <pcmcia/cistpl.h>
481da177e4SLinus Torvalds #include <pcmcia/ciscode.h>
491da177e4SLinus Torvalds #include <pcmcia/ds.h>
501da177e4SLinus Torvalds #include <pcmcia/cisreg.h>
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds #include <net/bluetooth/bluetooth.h>
531da177e4SLinus Torvalds #include <net/bluetooth/hci_core.h>
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds /* ======================== Module parameters ======================== */
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 
6063fbd24eSMarcel Holtmann MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
611da177e4SLinus Torvalds MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
621da177e4SLinus Torvalds MODULE_LICENSE("GPL");
632312119aSMarcel Holtmann MODULE_FIRMWARE("BT3CPCC.bin");
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds /* ======================== Local structures ======================== */
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds 
703bbaf812SHimangi Saraogi struct bt3c_info {
71fd238232SDominik Brodowski 	struct pcmcia_device *p_dev;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	struct hci_dev *hdev;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	spinlock_t lock;		/* For serializing operations */
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	struct sk_buff_head txq;
781da177e4SLinus Torvalds 	unsigned long tx_state;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	unsigned long rx_state;
811da177e4SLinus Torvalds 	unsigned long rx_count;
821da177e4SLinus Torvalds 	struct sk_buff *rx_skb;
833bbaf812SHimangi Saraogi };
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 
8615b99ac1SDominik Brodowski static int bt3c_config(struct pcmcia_device *link);
87fba395eeSDominik Brodowski static void bt3c_release(struct pcmcia_device *link);
881da177e4SLinus Torvalds 
89cc3b4866SDominik Brodowski static void bt3c_detach(struct pcmcia_device *p_dev);
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds /* Transmit states  */
931da177e4SLinus Torvalds #define XMIT_SENDING  1
941da177e4SLinus Torvalds #define XMIT_WAKEUP   2
951da177e4SLinus Torvalds #define XMIT_WAITING  8
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds /* Receiver states */
981da177e4SLinus Torvalds #define RECV_WAIT_PACKET_TYPE   0
991da177e4SLinus Torvalds #define RECV_WAIT_EVENT_HEADER  1
1001da177e4SLinus Torvalds #define RECV_WAIT_ACL_HEADER    2
1011da177e4SLinus Torvalds #define RECV_WAIT_SCO_HEADER    3
1021da177e4SLinus Torvalds #define RECV_WAIT_DATA          4
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds /* ======================== Special I/O functions ======================== */
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds #define DATA_L   0
1101da177e4SLinus Torvalds #define DATA_H   1
1111da177e4SLinus Torvalds #define ADDR_L   2
1121da177e4SLinus Torvalds #define ADDR_H   3
1131da177e4SLinus Torvalds #define CONTROL  4
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 
bt3c_address(unsigned int iobase,unsigned short addr)1161da177e4SLinus Torvalds static inline void bt3c_address(unsigned int iobase, unsigned short addr)
1171da177e4SLinus Torvalds {
1181da177e4SLinus Torvalds 	outb(addr & 0xff, iobase + ADDR_L);
1191da177e4SLinus Torvalds 	outb((addr >> 8) & 0xff, iobase + ADDR_H);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 
bt3c_put(unsigned int iobase,unsigned short value)1231da177e4SLinus Torvalds static inline void bt3c_put(unsigned int iobase, unsigned short value)
1241da177e4SLinus Torvalds {
1251da177e4SLinus Torvalds 	outb(value & 0xff, iobase + DATA_L);
1261da177e4SLinus Torvalds 	outb((value >> 8) & 0xff, iobase + DATA_H);
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 
bt3c_io_write(unsigned int iobase,unsigned short addr,unsigned short value)1301da177e4SLinus Torvalds static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
1311da177e4SLinus Torvalds {
1321da177e4SLinus Torvalds 	bt3c_address(iobase, addr);
1331da177e4SLinus Torvalds 	bt3c_put(iobase, value);
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 
bt3c_get(unsigned int iobase)1371da177e4SLinus Torvalds static inline unsigned short bt3c_get(unsigned int iobase)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds 	unsigned short value = inb(iobase + DATA_L);
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	value |= inb(iobase + DATA_H) << 8;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	return value;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 
bt3c_read(unsigned int iobase,unsigned short addr)1471da177e4SLinus Torvalds static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
1481da177e4SLinus Torvalds {
1491da177e4SLinus Torvalds 	bt3c_address(iobase, addr);
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	return bt3c_get(iobase);
1521da177e4SLinus Torvalds }
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds /* ======================== Interrupt handling ======================== */
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 
bt3c_write(unsigned int iobase,int fifo_size,__u8 * buf,int len)1591da177e4SLinus Torvalds static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
1601da177e4SLinus Torvalds {
1611da177e4SLinus Torvalds 	int actual = 0;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	bt3c_address(iobase, 0x7080);
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	/* Fill FIFO with current frame */
1661da177e4SLinus Torvalds 	while (actual < len) {
1671da177e4SLinus Torvalds 		/* Transmit next byte */
1681da177e4SLinus Torvalds 		bt3c_put(iobase, buf[actual]);
1691da177e4SLinus Torvalds 		actual++;
1701da177e4SLinus Torvalds 	}
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x7005, actual);
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	return actual;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 
bt3c_write_wakeup(struct bt3c_info * info)1783bbaf812SHimangi Saraogi static void bt3c_write_wakeup(struct bt3c_info *info)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	if (!info) {
1811da177e4SLinus Torvalds 		BT_ERR("Unknown device");
1821da177e4SLinus Torvalds 		return;
1831da177e4SLinus Torvalds 	}
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
1861da177e4SLinus Torvalds 		return;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	do {
189fc5fef61SGustavo Padovan 		unsigned int iobase = info->p_dev->resource[0]->start;
1901da177e4SLinus Torvalds 		register struct sk_buff *skb;
191fc5fef61SGustavo Padovan 		int len;
1921da177e4SLinus Torvalds 
193e2d40963SDominik Brodowski 		if (!pcmcia_dev_present(info->p_dev))
1941da177e4SLinus Torvalds 			break;
1951da177e4SLinus Torvalds 
196a08b15e6SValentin Ilie 		skb = skb_dequeue(&(info->txq));
197a08b15e6SValentin Ilie 		if (!skb) {
1981da177e4SLinus Torvalds 			clear_bit(XMIT_SENDING, &(info->tx_state));
1991da177e4SLinus Torvalds 			break;
2001da177e4SLinus Torvalds 		}
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 		/* Send frame */
2031da177e4SLinus Torvalds 		len = bt3c_write(iobase, 256, skb->data, skb->len);
2041da177e4SLinus Torvalds 
205aebcecccSPrasanna Karthik 		if (len != skb->len)
2061da177e4SLinus Torvalds 			BT_ERR("Very strange");
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 		kfree_skb(skb);
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 		info->hdev->stat.byte_tx += len;
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	} while (0);
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 
bt3c_receive(struct bt3c_info * info)2163bbaf812SHimangi Saraogi static void bt3c_receive(struct bt3c_info *info)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds 	unsigned int iobase;
2191da177e4SLinus Torvalds 	int size = 0, avail;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	if (!info) {
2221da177e4SLinus Torvalds 		BT_ERR("Unknown device");
2231da177e4SLinus Torvalds 		return;
2241da177e4SLinus Torvalds 	}
2251da177e4SLinus Torvalds 
2269a017a91SDominik Brodowski 	iobase = info->p_dev->resource[0]->start;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	avail = bt3c_read(iobase, 0x7006);
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	bt3c_address(iobase, 0x7480);
2311da177e4SLinus Torvalds 	while (size < avail) {
2321da177e4SLinus Torvalds 		size++;
2331da177e4SLinus Torvalds 		info->hdev->stat.byte_rx++;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 		/* Allocate packet */
2367a1dc788SPrasanna Karthik 		if (!info->rx_skb) {
2371da177e4SLinus Torvalds 			info->rx_state = RECV_WAIT_PACKET_TYPE;
2381da177e4SLinus Torvalds 			info->rx_count = 0;
239a08b15e6SValentin Ilie 			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
240a08b15e6SValentin Ilie 			if (!info->rx_skb) {
2411da177e4SLinus Torvalds 				BT_ERR("Can't allocate mem for new packet");
2421da177e4SLinus Torvalds 				return;
2431da177e4SLinus Torvalds 			}
2441da177e4SLinus Torvalds 		}
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds 		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
2481da177e4SLinus Torvalds 
249618e8bc2SMarcel Holtmann 			hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L);
2501da177e4SLinus Torvalds 			inb(iobase + DATA_H);
2511da177e4SLinus Torvalds 
252618e8bc2SMarcel Holtmann 			switch (hci_skb_pkt_type(info->rx_skb)) {
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 			case HCI_EVENT_PKT:
2551da177e4SLinus Torvalds 				info->rx_state = RECV_WAIT_EVENT_HEADER;
2561da177e4SLinus Torvalds 				info->rx_count = HCI_EVENT_HDR_SIZE;
2571da177e4SLinus Torvalds 				break;
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 			case HCI_ACLDATA_PKT:
2601da177e4SLinus Torvalds 				info->rx_state = RECV_WAIT_ACL_HEADER;
2611da177e4SLinus Torvalds 				info->rx_count = HCI_ACL_HDR_SIZE;
2621da177e4SLinus Torvalds 				break;
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 			case HCI_SCODATA_PKT:
2651da177e4SLinus Torvalds 				info->rx_state = RECV_WAIT_SCO_HEADER;
2661da177e4SLinus Torvalds 				info->rx_count = HCI_SCO_HDR_SIZE;
2671da177e4SLinus Torvalds 				break;
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 			default:
2701da177e4SLinus Torvalds 				/* Unknown packet */
271618e8bc2SMarcel Holtmann 				BT_ERR("Unknown HCI packet with type 0x%02x received",
272618e8bc2SMarcel Holtmann 				       hci_skb_pkt_type(info->rx_skb));
2731da177e4SLinus Torvalds 				info->hdev->stat.err_rx++;
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 				kfree_skb(info->rx_skb);
2761da177e4SLinus Torvalds 				info->rx_skb = NULL;
2771da177e4SLinus Torvalds 				break;
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 			}
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 		} else {
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 			__u8 x = inb(iobase + DATA_L);
2841da177e4SLinus Torvalds 
285634fef61SJohannes Berg 			skb_put_u8(info->rx_skb, x);
2861da177e4SLinus Torvalds 			inb(iobase + DATA_H);
2871da177e4SLinus Torvalds 			info->rx_count--;
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds 			if (info->rx_count == 0) {
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 				int dlen;
2921da177e4SLinus Torvalds 				struct hci_event_hdr *eh;
2931da177e4SLinus Torvalds 				struct hci_acl_hdr *ah;
2941da177e4SLinus Torvalds 				struct hci_sco_hdr *sh;
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 				switch (info->rx_state) {
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 				case RECV_WAIT_EVENT_HEADER:
2992a123b86SArnaldo Carvalho de Melo 					eh = hci_event_hdr(info->rx_skb);
3001da177e4SLinus Torvalds 					info->rx_state = RECV_WAIT_DATA;
3011da177e4SLinus Torvalds 					info->rx_count = eh->plen;
3021da177e4SLinus Torvalds 					break;
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 				case RECV_WAIT_ACL_HEADER:
3052a123b86SArnaldo Carvalho de Melo 					ah = hci_acl_hdr(info->rx_skb);
3061da177e4SLinus Torvalds 					dlen = __le16_to_cpu(ah->dlen);
3071da177e4SLinus Torvalds 					info->rx_state = RECV_WAIT_DATA;
3081da177e4SLinus Torvalds 					info->rx_count = dlen;
3091da177e4SLinus Torvalds 					break;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 				case RECV_WAIT_SCO_HEADER:
3122a123b86SArnaldo Carvalho de Melo 					sh = hci_sco_hdr(info->rx_skb);
3131da177e4SLinus Torvalds 					info->rx_state = RECV_WAIT_DATA;
3141da177e4SLinus Torvalds 					info->rx_count = sh->dlen;
3151da177e4SLinus Torvalds 					break;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 				case RECV_WAIT_DATA:
318e1a26170SMarcel Holtmann 					hci_recv_frame(info->hdev, info->rx_skb);
3191da177e4SLinus Torvalds 					info->rx_skb = NULL;
3201da177e4SLinus Torvalds 					break;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 				}
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 			}
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 		}
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	}
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x7006, 0x0000);
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 
bt3c_interrupt(int irq,void * dev_inst)3347d12e780SDavid Howells static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
3351da177e4SLinus Torvalds {
3363bbaf812SHimangi Saraogi 	struct bt3c_info *info = dev_inst;
3371da177e4SLinus Torvalds 	unsigned int iobase;
3381da177e4SLinus Torvalds 	int iir;
339aafcf998SAlan Cox 	irqreturn_t r = IRQ_NONE;
3401da177e4SLinus Torvalds 
3417427847dSMike Frysinger 	if (!info || !info->hdev)
3427427847dSMike Frysinger 		/* our irq handler is shared */
3437427847dSMike Frysinger 		return IRQ_NONE;
3441da177e4SLinus Torvalds 
3459a017a91SDominik Brodowski 	iobase = info->p_dev->resource[0]->start;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	spin_lock(&(info->lock));
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	iir = inb(iobase + CONTROL);
3501da177e4SLinus Torvalds 	if (iir & 0x80) {
3511da177e4SLinus Torvalds 		int stat = bt3c_read(iobase, 0x7001);
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 		if ((stat & 0xff) == 0x7f) {
3541da177e4SLinus Torvalds 			BT_ERR("Very strange (stat=0x%04x)", stat);
3551da177e4SLinus Torvalds 		} else if ((stat & 0xff) != 0xff) {
3561da177e4SLinus Torvalds 			if (stat & 0x0020) {
35734a55edaSAndre Haupt 				int status = bt3c_read(iobase, 0x7002) & 0x10;
3582064ee33SMarcel Holtmann 				bt_dev_info(info->hdev, "Antenna %s",
35934a55edaSAndre Haupt 							status ? "out" : "in");
3601da177e4SLinus Torvalds 			}
3611da177e4SLinus Torvalds 			if (stat & 0x0001)
3621da177e4SLinus Torvalds 				bt3c_receive(info);
3631da177e4SLinus Torvalds 			if (stat & 0x0002) {
3641da177e4SLinus Torvalds 				clear_bit(XMIT_SENDING, &(info->tx_state));
3651da177e4SLinus Torvalds 				bt3c_write_wakeup(info);
3661da177e4SLinus Torvalds 			}
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 			bt3c_io_write(iobase, 0x7001, 0x0000);
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 			outb(iir, iobase + CONTROL);
3711da177e4SLinus Torvalds 		}
372aafcf998SAlan Cox 		r = IRQ_HANDLED;
3731da177e4SLinus Torvalds 	}
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds 	spin_unlock(&(info->lock));
3761da177e4SLinus Torvalds 
377aafcf998SAlan Cox 	return r;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds /* ======================== HCI interface ======================== */
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 
bt3c_hci_flush(struct hci_dev * hdev)3851da177e4SLinus Torvalds static int bt3c_hci_flush(struct hci_dev *hdev)
3861da177e4SLinus Torvalds {
3873bbaf812SHimangi Saraogi 	struct bt3c_info *info = hci_get_drvdata(hdev);
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	/* Drop TX queue */
3901da177e4SLinus Torvalds 	skb_queue_purge(&(info->txq));
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 	return 0;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 
bt3c_hci_open(struct hci_dev * hdev)3961da177e4SLinus Torvalds static int bt3c_hci_open(struct hci_dev *hdev)
3971da177e4SLinus Torvalds {
3981da177e4SLinus Torvalds 	return 0;
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 
bt3c_hci_close(struct hci_dev * hdev)4021da177e4SLinus Torvalds static int bt3c_hci_close(struct hci_dev *hdev)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds 	bt3c_hci_flush(hdev);
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 	return 0;
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 
bt3c_hci_send_frame(struct hci_dev * hdev,struct sk_buff * skb)4107bd8f09fSMarcel Holtmann static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
4111da177e4SLinus Torvalds {
4123bbaf812SHimangi Saraogi 	struct bt3c_info *info = hci_get_drvdata(hdev);
4131da177e4SLinus Torvalds 	unsigned long flags;
4141da177e4SLinus Torvalds 
415618e8bc2SMarcel Holtmann 	switch (hci_skb_pkt_type(skb)) {
4161da177e4SLinus Torvalds 	case HCI_COMMAND_PKT:
4171da177e4SLinus Torvalds 		hdev->stat.cmd_tx++;
4181da177e4SLinus Torvalds 		break;
4191da177e4SLinus Torvalds 	case HCI_ACLDATA_PKT:
4201da177e4SLinus Torvalds 		hdev->stat.acl_tx++;
4211da177e4SLinus Torvalds 		break;
4221da177e4SLinus Torvalds 	case HCI_SCODATA_PKT:
4231da177e4SLinus Torvalds 		hdev->stat.sco_tx++;
4241da177e4SLinus Torvalds 		break;
425f104f06cSPrasanna Karthik 	}
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	/* Prepend skb with frame type */
428618e8bc2SMarcel Holtmann 	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
4291da177e4SLinus Torvalds 	skb_queue_tail(&(info->txq), skb);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	spin_lock_irqsave(&(info->lock), flags);
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	bt3c_write_wakeup(info);
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	spin_unlock_irqrestore(&(info->lock), flags);
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	return 0;
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds /* ======================== Card services HCI interaction ======================== */
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 
bt3c_load_firmware(struct bt3c_info * info,const unsigned char * firmware,int count)4453bbaf812SHimangi Saraogi static int bt3c_load_firmware(struct bt3c_info *info,
4463bbaf812SHimangi Saraogi 			      const unsigned char *firmware,
4478187b4fbSDavid Woodhouse 			      int count)
4481da177e4SLinus Torvalds {
4491da177e4SLinus Torvalds 	char *ptr = (char *) firmware;
4501da177e4SLinus Torvalds 	char b[9];
451*38561350SDing Xiang 	unsigned int iobase, tmp, tn;
45210bd9731SPrasanna Karthik 	unsigned long size, addr, fcs;
4531da177e4SLinus Torvalds 	int i, err = 0;
4541da177e4SLinus Torvalds 
4559a017a91SDominik Brodowski 	iobase = info->p_dev->resource[0]->start;
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	/* Reset */
4581da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x8040, 0x0404);
4591da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x8040, 0x0400);
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	udelay(1);
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x8040, 0x0404);
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	udelay(17);
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	/* Load */
4681da177e4SLinus Torvalds 	while (count) {
4691da177e4SLinus Torvalds 		if (ptr[0] != 'S') {
4701da177e4SLinus Torvalds 			BT_ERR("Bad address in firmware");
4711da177e4SLinus Torvalds 			err = -EFAULT;
4721da177e4SLinus Torvalds 			goto error;
4731da177e4SLinus Torvalds 		}
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 		memset(b, 0, sizeof(b));
4761da177e4SLinus Torvalds 		memcpy(b, ptr + 2, 2);
47710bd9731SPrasanna Karthik 		if (kstrtoul(b, 16, &size) < 0)
47810bd9731SPrasanna Karthik 			return -EINVAL;
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds 		memset(b, 0, sizeof(b));
4811da177e4SLinus Torvalds 		memcpy(b, ptr + 4, 8);
48210bd9731SPrasanna Karthik 		if (kstrtoul(b, 16, &addr) < 0)
48310bd9731SPrasanna Karthik 			return -EINVAL;
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 		memset(b, 0, sizeof(b));
4861da177e4SLinus Torvalds 		memcpy(b, ptr + (size * 2) + 2, 2);
48710bd9731SPrasanna Karthik 		if (kstrtoul(b, 16, &fcs) < 0)
48810bd9731SPrasanna Karthik 			return -EINVAL;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 		memset(b, 0, sizeof(b));
4911da177e4SLinus Torvalds 		for (tmp = 0, i = 0; i < size; i++) {
4921da177e4SLinus Torvalds 			memcpy(b, ptr + (i * 2) + 2, 2);
493*38561350SDing Xiang 			if (kstrtouint(b, 16, &tn))
494*38561350SDing Xiang 				return -EINVAL;
495*38561350SDing Xiang 			tmp += tn;
4961da177e4SLinus Torvalds 		}
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds 		if (((tmp + fcs) & 0xff) != 0xff) {
4991da177e4SLinus Torvalds 			BT_ERR("Checksum error in firmware");
5001da177e4SLinus Torvalds 			err = -EILSEQ;
5011da177e4SLinus Torvalds 			goto error;
5021da177e4SLinus Torvalds 		}
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds 		if (ptr[1] == '3') {
5051da177e4SLinus Torvalds 			bt3c_address(iobase, addr);
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 			memset(b, 0, sizeof(b));
5081da177e4SLinus Torvalds 			for (i = 0; i < (size - 4) / 2; i++) {
5091da177e4SLinus Torvalds 				memcpy(b, ptr + (i * 4) + 12, 4);
510*38561350SDing Xiang 				if (kstrtouint(b, 16, &tmp))
511*38561350SDing Xiang 					return -EINVAL;
5121da177e4SLinus Torvalds 				bt3c_put(iobase, tmp);
5131da177e4SLinus Torvalds 			}
5141da177e4SLinus Torvalds 		}
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 		ptr   += (size * 2) + 6;
5171da177e4SLinus Torvalds 		count -= (size * 2) + 6;
5181da177e4SLinus Torvalds 	}
5191da177e4SLinus Torvalds 
5201da177e4SLinus Torvalds 	udelay(17);
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	/* Boot */
5231da177e4SLinus Torvalds 	bt3c_address(iobase, 0x3000);
5241da177e4SLinus Torvalds 	outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds error:
5271da177e4SLinus Torvalds 	udelay(17);
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds 	/* Clear */
5301da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x7006, 0x0000);
5311da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x7005, 0x0000);
5321da177e4SLinus Torvalds 	bt3c_io_write(iobase, 0x7001, 0x0000);
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	return err;
5351da177e4SLinus Torvalds }
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 
bt3c_open(struct bt3c_info * info)5383bbaf812SHimangi Saraogi static int bt3c_open(struct bt3c_info *info)
5391da177e4SLinus Torvalds {
5401da177e4SLinus Torvalds 	const struct firmware *firmware;
5411da177e4SLinus Torvalds 	struct hci_dev *hdev;
5421da177e4SLinus Torvalds 	int err;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	spin_lock_init(&(info->lock));
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds 	skb_queue_head_init(&(info->txq));
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	info->rx_state = RECV_WAIT_PACKET_TYPE;
5491da177e4SLinus Torvalds 	info->rx_count = 0;
5501da177e4SLinus Torvalds 	info->rx_skb = NULL;
5511da177e4SLinus Torvalds 
5521da177e4SLinus Torvalds 	/* Initialize HCI device */
5531da177e4SLinus Torvalds 	hdev = hci_alloc_dev();
5541da177e4SLinus Torvalds 	if (!hdev) {
5551da177e4SLinus Torvalds 		BT_ERR("Can't allocate HCI device");
5561da177e4SLinus Torvalds 		return -ENOMEM;
5571da177e4SLinus Torvalds 	}
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	info->hdev = hdev;
5601da177e4SLinus Torvalds 
561c13854ceSMarcel Holtmann 	hdev->bus = HCI_PCCARD;
562155961e8SDavid Herrmann 	hci_set_drvdata(hdev, info);
56327d35284SMarcel Holtmann 	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	hdev->open  = bt3c_hci_open;
5661da177e4SLinus Torvalds 	hdev->close = bt3c_hci_close;
5671da177e4SLinus Torvalds 	hdev->flush = bt3c_hci_flush;
5681da177e4SLinus Torvalds 	hdev->send  = bt3c_hci_send_frame;
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 	/* Load firmware */
571fd238232SDominik Brodowski 	err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
5721da177e4SLinus Torvalds 	if (err < 0) {
5731da177e4SLinus Torvalds 		BT_ERR("Firmware request failed");
5741da177e4SLinus Torvalds 		goto error;
5751da177e4SLinus Torvalds 	}
5761da177e4SLinus Torvalds 
5771da177e4SLinus Torvalds 	err = bt3c_load_firmware(info, firmware->data, firmware->size);
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds 	release_firmware(firmware);
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	if (err < 0) {
5821da177e4SLinus Torvalds 		BT_ERR("Firmware loading failed");
5831da177e4SLinus Torvalds 		goto error;
5841da177e4SLinus Torvalds 	}
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 	/* Timeout before it is safe to send the first HCI packet */
5871da177e4SLinus Torvalds 	msleep(1000);
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	/* Register HCI device */
5901da177e4SLinus Torvalds 	err = hci_register_dev(hdev);
5911da177e4SLinus Torvalds 	if (err < 0) {
5921da177e4SLinus Torvalds 		BT_ERR("Can't register HCI device");
5931da177e4SLinus Torvalds 		goto error;
5941da177e4SLinus Torvalds 	}
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 	return 0;
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds error:
5991da177e4SLinus Torvalds 	info->hdev = NULL;
6001da177e4SLinus Torvalds 	hci_free_dev(hdev);
6011da177e4SLinus Torvalds 	return err;
6021da177e4SLinus Torvalds }
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 
bt3c_close(struct bt3c_info * info)6053bbaf812SHimangi Saraogi static int bt3c_close(struct bt3c_info *info)
6061da177e4SLinus Torvalds {
6071da177e4SLinus Torvalds 	struct hci_dev *hdev = info->hdev;
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	if (!hdev)
6101da177e4SLinus Torvalds 		return -ENODEV;
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	bt3c_hci_close(hdev);
6131da177e4SLinus Torvalds 
61413ea4015SDavid Herrmann 	hci_unregister_dev(hdev);
6151da177e4SLinus Torvalds 	hci_free_dev(hdev);
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 	return 0;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
bt3c_probe(struct pcmcia_device * link)62015b99ac1SDominik Brodowski static int bt3c_probe(struct pcmcia_device *link)
6211da177e4SLinus Torvalds {
6223bbaf812SHimangi Saraogi 	struct bt3c_info *info;
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	/* Create new info device */
6254f61cb18SSachin Kamat 	info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
6261da177e4SLinus Torvalds 	if (!info)
627f8cfa618SDominik Brodowski 		return -ENOMEM;
6281da177e4SLinus Torvalds 
629fba395eeSDominik Brodowski 	info->p_dev = link;
6301da177e4SLinus Torvalds 	link->priv = info;
6311da177e4SLinus Torvalds 
63200990e7cSDominik Brodowski 	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
63300990e7cSDominik Brodowski 		CONF_AUTO_SET_IO;
6341da177e4SLinus Torvalds 
63515b99ac1SDominik Brodowski 	return bt3c_config(link);
6361da177e4SLinus Torvalds }
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds 
bt3c_detach(struct pcmcia_device * link)639fba395eeSDominik Brodowski static void bt3c_detach(struct pcmcia_device *link)
6401da177e4SLinus Torvalds {
6411da177e4SLinus Torvalds 	bt3c_release(link);
6421da177e4SLinus Torvalds }
6431da177e4SLinus Torvalds 
bt3c_check_config(struct pcmcia_device * p_dev,void * priv_data)64400990e7cSDominik Brodowski static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data)
6451da177e4SLinus Torvalds {
64600990e7cSDominik Brodowski 	int *try = priv_data;
64790abdc3bSDominik Brodowski 
648510df251SAndrei Emeltchenko 	if (!try)
64900990e7cSDominik Brodowski 		p_dev->io_lines = 16;
65000990e7cSDominik Brodowski 
65100990e7cSDominik Brodowski 	if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
65200990e7cSDominik Brodowski 		return -EINVAL;
65300990e7cSDominik Brodowski 
65400990e7cSDominik Brodowski 	p_dev->resource[0]->end = 8;
65500990e7cSDominik Brodowski 	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
65600990e7cSDominik Brodowski 	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
65700990e7cSDominik Brodowski 
65800990e7cSDominik Brodowski 	return pcmcia_request_io(p_dev);
6591da177e4SLinus Torvalds }
6601da177e4SLinus Torvalds 
bt3c_check_config_notpicky(struct pcmcia_device * p_dev,void * priv_data)661ed58872aSDominik Brodowski static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
662ed58872aSDominik Brodowski 				      void *priv_data)
6631da177e4SLinus Torvalds {
664ed58872aSDominik Brodowski 	static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
665ed58872aSDominik Brodowski 	int j;
6661da177e4SLinus Torvalds 
66700990e7cSDominik Brodowski 	if (p_dev->io_lines > 3)
66800990e7cSDominik Brodowski 		return -ENODEV;
66900990e7cSDominik Brodowski 
67000990e7cSDominik Brodowski 	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
67100990e7cSDominik Brodowski 	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
67200990e7cSDominik Brodowski 	p_dev->resource[0]->end = 8;
67300990e7cSDominik Brodowski 
674ed58872aSDominik Brodowski 	for (j = 0; j < 5; j++) {
67590abdc3bSDominik Brodowski 		p_dev->resource[0]->start = base[j];
67690abdc3bSDominik Brodowski 		p_dev->io_lines = base[j] ? 16 : 3;
67790abdc3bSDominik Brodowski 		if (!pcmcia_request_io(p_dev))
678ed58872aSDominik Brodowski 			return 0;
679ed58872aSDominik Brodowski 	}
680ed58872aSDominik Brodowski 	return -ENODEV;
6811da177e4SLinus Torvalds }
6821da177e4SLinus Torvalds 
bt3c_config(struct pcmcia_device * link)68315b99ac1SDominik Brodowski static int bt3c_config(struct pcmcia_device *link)
6841da177e4SLinus Torvalds {
6853bbaf812SHimangi Saraogi 	struct bt3c_info *info = link->priv;
686ed58872aSDominik Brodowski 	int i;
687ed58872aSDominik Brodowski 	unsigned long try;
6881da177e4SLinus Torvalds 
689ed58872aSDominik Brodowski 	/* First pass: look for a config entry that looks normal.
690d98422cbSDerek Robson 	 * Two tries: without IO aliases, then with aliases
691d98422cbSDerek Robson 	 */
692ed58872aSDominik Brodowski 	for (try = 0; try < 2; try++)
693ed58872aSDominik Brodowski 		if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try))
6941da177e4SLinus Torvalds 			goto found_port;
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 	/* Second pass: try to find an entry that isn't picky about
697d98422cbSDerek Robson 	 * its base address, then try to grab any standard serial port
698d98422cbSDerek Robson 	 * address, and finally try to get any free port.
699d98422cbSDerek Robson 	 */
700ed58872aSDominik Brodowski 	if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL))
7011da177e4SLinus Torvalds 		goto found_port;
702ed58872aSDominik Brodowski 
703ed58872aSDominik Brodowski 	BT_ERR("No usable port range found");
704ed58872aSDominik Brodowski 	goto failed;
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds found_port:
707eb14120fSDominik Brodowski 	i = pcmcia_request_irq(link, &bt3c_interrupt);
7089ac3e58cSDominik Brodowski 	if (i != 0)
709eb14120fSDominik Brodowski 		goto failed;
7101da177e4SLinus Torvalds 
7111ac71e5aSDominik Brodowski 	i = pcmcia_enable_device(link);
7129ac3e58cSDominik Brodowski 	if (i != 0)
7131da177e4SLinus Torvalds 		goto failed;
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds 	if (bt3c_open(info) != 0)
7161da177e4SLinus Torvalds 		goto failed;
7171da177e4SLinus Torvalds 
71815b99ac1SDominik Brodowski 	return 0;
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds failed:
7211da177e4SLinus Torvalds 	bt3c_release(link);
72215b99ac1SDominik Brodowski 	return -ENODEV;
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds 
bt3c_release(struct pcmcia_device * link)726fba395eeSDominik Brodowski static void bt3c_release(struct pcmcia_device *link)
7271da177e4SLinus Torvalds {
7283bbaf812SHimangi Saraogi 	struct bt3c_info *info = link->priv;
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds 	bt3c_close(info);
7311da177e4SLinus Torvalds 
732fba395eeSDominik Brodowski 	pcmcia_disable_device(link);
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 
73625f8f54fSJoe Perches static const struct pcmcia_device_id bt3c_ids[] = {
737a01c3ed4SDominik Brodowski 	PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
738a01c3ed4SDominik Brodowski 	PCMCIA_DEVICE_NULL
739a01c3ed4SDominik Brodowski };
740a01c3ed4SDominik Brodowski MODULE_DEVICE_TABLE(pcmcia, bt3c_ids);
741a01c3ed4SDominik Brodowski 
7421da177e4SLinus Torvalds static struct pcmcia_driver bt3c_driver = {
7431da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
7441da177e4SLinus Torvalds 	.name		= "bt3c_cs",
74515b99ac1SDominik Brodowski 	.probe		= bt3c_probe,
746cc3b4866SDominik Brodowski 	.remove		= bt3c_detach,
747a01c3ed4SDominik Brodowski 	.id_table	= bt3c_ids,
7481da177e4SLinus Torvalds };
749e0c005f4SH Hartley Sweeten module_pcmcia_driver(bt3c_driver);
750