1c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/*****************************************************************************
2c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*
3c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz* Filename:      mcs7780.c
4c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz* Version:       0.4-alpha
5c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz* Description:   Irda MosChip USB Dongle Driver
6c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz* Authors:       Lukasz Stelmach <stlman@poczta.fm>
7c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz* 		 Brian Pugh <bpugh@cs.pdx.edu>
8c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*		 Judy Fischbach <jfisch@cs.pdx.edu>
9c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*
10c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Based on stir4200 driver, but some things done differently.
11c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Based on earlier driver by Paul Stewart <stewart@parc.com>
12c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*
13c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>
14c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Copyright (C) 2001, Dag Brattli <dag@brattli.net>
15c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
16c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>
17c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm>
18c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu>
19c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Copyright (C) 2005, Judy Fischbach <jfisch@cs.pdx.edu>
20c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*
21c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       This program is free software; you can redistribute it and/or modify
22c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       it under the terms of the GNU General Public License as published by
23c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       the Free Software Foundation; either version 2 of the License, or
24c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       (at your option) any later version.
25c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*
26c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       This program is distributed in the hope that it will be useful,
27c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       but WITHOUT ANY WARRANTY; without even the implied warranty of
28c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       GNU General Public License for more details.
30c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*
31c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       You should have received a copy of the GNU General Public License
32c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       along with this program; if not, write to the Free Software
33c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*
35c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*****************************************************************************/
36c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
37c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/*
38c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither
39c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * compatibile with irda-usb nor with stir4200. Although it is quite
40c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * similar to the later as far as general idea of operation is concerned.
41c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * That is it requires the software to do all the framing job at SIR speeds.
42c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * The hardware does take care of the framing at MIR and FIR speeds.
43c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * It supports all speeds from 2400 through 4Mbps
44c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
45c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
46c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/module.h>
47c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/moduleparam.h>
48c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/kernel.h>
49c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/types.h>
50c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/errno.h>
51c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/init.h>
52c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/slab.h>
53c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/usb.h>
54c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/device.h>
55c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <linux/crc32.h>
56c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
57c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <asm/unaligned.h>
58c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <asm/byteorder.h>
59c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <asm/uaccess.h>
60c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
61c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <net/irda/irda.h>
62c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <net/irda/wrapper.h>
63c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include <net/irda/crc.h>
64c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
65c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#include "mcs7780.h"
66c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
67c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#define MCS_VENDOR_ID 0x9710
68c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz#define MCS_PRODUCT_ID 0x7780
69c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
70c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic struct usb_device_id mcs_table[] = {
71c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* MosChip Corp.,  MCS7780 FIR-USB Adapter */
72c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	{USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)},
73c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	{},
74c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz};
75c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
76c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_AUTHOR("Brian Pugh <bpugh@cs.pdx.edu>");
77c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780");
78c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_VERSION("0.3alpha");
79c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_LICENSE("GPL");
80c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
81c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_DEVICE_TABLE(usb, mcs_table);
82c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
83c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int qos_mtt_bits = 0x07 /* > 1ms */ ;
84c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizmodule_param(qos_mtt_bits, int, 0);
85c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
86c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
87c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int receive_mode = 0x1;
88c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizmodule_param(receive_mode, int, 0);
89c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_PARM_DESC(receive_mode,
90c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		 "Receive mode of the device (1:fast, 0:slow, default:1)");
91c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
92c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int sir_tweak = 1;
93c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizmodule_param(sir_tweak, int, 0444);
94c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_PARM_DESC(sir_tweak,
95c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		 "Default pulse width (1:1.6us, 0:3/16 bit, default:1).");
96c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
97c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int transceiver_type = MCS_TSC_VISHAY;
98c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizmodule_param(transceiver_type, int, 0444);
99c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel OrtizMODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h.");
100c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
1017263ade1e1e72e34fc3c179f3aeaa07a11872d22Adrian Bunkstatic struct usb_driver mcs_driver = {
102c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	.name = "mcs7780",
103c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	.probe = mcs_probe,
104c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	.disconnect = mcs_disconnect,
105c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	.id_table = mcs_table,
106c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz};
107c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
108c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* speed flag selection by direct addressing.
109c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizaddr = (speed >> 8) & 0x0f
110c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
111c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz0x1   57600	 0x2  115200	 0x4 1152000	 0x5    9600
112c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz0x6   38400	 0x9    2400	 0xa  576000	 0xb   19200
113c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
114c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz4Mbps (or 2400) must be checked separately. Since it also has
115c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizto be programmed in a different manner that is not a big problem.
116c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz*/
117c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic __u16 mcs_speed_set[16] = { 0,
118c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_57600,
119c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_115200,
120c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	0,
121c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_1152000,
122c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_9600,
123c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_38400,
124c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	0, 0,
125c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_2400,
126c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_576000,
127c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	MCS_SPEED_19200,
128c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	0, 0, 0,
129c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz};
130c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
131c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Set given 16 bit register with a 16 bit value. Send control message
132c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * to set dongle register. */
133c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val)
134c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
135c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct usb_device *dev = mcs->usbdev;
136c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
137c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			       MCS_WR_RTYPE, val, reg, NULL, 0,
138c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			       msecs_to_jiffies(MCS_CTRL_TIMEOUT));
139c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
140c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
141c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Get 16 bit register value. Send contol message to read dongle register. */
142c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val)
143c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
144c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct usb_device *dev = mcs->usbdev;
145c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
146c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				  MCS_RD_RTYPE, 0, reg, val, 2,
147c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				  msecs_to_jiffies(MCS_CTRL_TIMEOUT));
148c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
149c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return ret;
150c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
151c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
152c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Setup a communication between mcs7780 and TFDU chips.  It is described
153c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * in more detail in the data sheet.  The setup sequence puts the the
154c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * vishay tranceiver into high speed mode.  It will also receive SIR speed
155c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * packets but at reduced sensitivity.
156c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
157c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
158c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* 0: OK 1:ERROR */
159c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs)
160c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
161c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
162c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u16 rval;
163c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
164c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* mcs_get_reg should read exactly two bytes from the dongle */
165c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval);
166c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret != 2)) {
167c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = -EIO;
168c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
169c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
170c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
171c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* The MCS_XCVR_CONF bit puts the transceiver into configuration
172c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * mode.  The MCS_MODE0 bit must start out high (1) and then
173c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * transition to low and the MCS_STFIR and MCS_MODE1 bits must
174c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * be low.
175c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 */
176c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval |= (MCS_MODE0 | MCS_XCVR_CONF);
177c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_STFIR;
178c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_MODE1;
179c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
180c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret))
181c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
182c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
183c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_MODE0;
184c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
185c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret))
186c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
187c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
188c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_XCVR_CONF;
189c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
190c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret))
191c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
192c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
193c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = 0;
194c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	error:
195c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return ret;
196c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
197c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
198c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Setup a communication between mcs7780 and agilent chip. */
199c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs)
200c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
201599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz	IRDA_WARNING("This transceiver type is not supported yet.\n");
202c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return 1;
203c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
204c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
205c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Setup a communication between mcs7780 and sharp chip. */
206c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs)
207c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
208599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz	IRDA_WARNING("This transceiver type is not supported yet.\n");
209c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return 1;
210c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
211c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
212c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Common setup for all transceivers */
213c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_setup_transceiver(struct mcs_cb *mcs)
214c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
215c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
216c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u16 rval;
217c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	char *msg;
218c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
219c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	msg = "Basic transceiver setup error.";
220c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
221c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* read value of MODE Register, set the DRIVER and RESET bits
222c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	* and write value back out to MODE Register
223c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	*/
224c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
225c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(ret != 2))
226c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
227c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval |= MCS_DRIVER;	/* put the mcs7780 into configuration mode. */
228c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
229c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(ret))
230c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
231c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
232c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval = 0;		/* set min pulse width to 0 initially. */
233c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval);
234c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(ret))
235c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
236c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
237c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
238c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(ret != 2))
239c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
240c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
241c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_FIR;	/* turn off fir mode. */
242c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(mcs->sir_tweak)
243c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		rval |= MCS_SIR16US;	/* 1.6us pulse width */
244c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	else
245c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		rval &= ~MCS_SIR16US;	/* 3/16 bit time pulse width */
246c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
247c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* make sure ask mode and back to back packets are off. */
248c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~(MCS_BBTG | MCS_ASK);
249c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
250c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_SPEED_MASK;
251c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval |= MCS_SPEED_9600;		/* make sure initial speed is 9600. */
252c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->speed = 9600;
253c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->new_speed = 0;		/* new_speed is set to 0 */
254c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_PLLPWDN;		/* disable power down. */
255c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
256c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* make sure device determines direction and that the auto send sip
257c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * pulse are on.
258c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 */
259c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval |= MCS_DTD | MCS_SIPEN;
260c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
261c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
262c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(ret))
263c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
264c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
265c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	msg = "transceiver model specific setup error.";
266c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	switch (mcs->transceiver_type) {
267c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	case MCS_TSC_VISHAY:
268c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = mcs_setup_transceiver_vishay(mcs);
269c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		break;
270c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
271c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	case MCS_TSC_SHARP:
272c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = mcs_setup_transceiver_sharp(mcs);
273c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		break;
274c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
275c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	case MCS_TSC_AGILENT:
276c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = mcs_setup_transceiver_agilent(mcs);
277c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		break;
278c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
279c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	default:
280599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz		IRDA_WARNING("Unknown transceiver type: %d\n",
281c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			     mcs->transceiver_type);
282c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = 1;
283c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
284c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret))
285c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
286c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
287c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* If transceiver is not SHARP, then if receive mode set
288c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	* on the RXFAST bit in the XCVR Register otherwise unset it
289c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	*/
290c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (mcs->transceiver_type != MCS_TSC_SHARP) {
291c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
292c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval);
293c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if (unlikely(ret != 2))
294c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			goto error;
295c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if (mcs->receive_mode)
296c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			rval |= MCS_RXFAST;
297c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		else
298c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			rval &= ~MCS_RXFAST;
299c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
300c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if (unlikely(ret))
301c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			goto error;
302c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
303c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
304c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	msg = "transceiver reset.";
305c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
306c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
307c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret != 2))
308c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
309c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
310c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* reset the mcs7780 so all changes take effect. */
311c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_RESET;
312c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
313c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret))
314c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
315c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	else
316c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return ret;
317c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
318c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizerror:
319599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz	IRDA_ERROR("%s\n", msg);
320c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return ret;
321c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
322c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
323c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Wraps the data in format for SIR */
324c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf)
325c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
326c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int wraplen;
327c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
328c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* 2: full frame length, including "the length" */
329c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	wraplen = async_wrap_skb(skb, buf + 2, 4094);
330c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
331c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	wraplen += 2;
332c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[0] = wraplen & 0xff;
333c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[1] = (wraplen >> 8) & 0xff;
334c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
335c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return wraplen;
336c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
337c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
338c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Wraps the data in format for FIR */
339c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf)
340c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
341c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	unsigned int len = 0;
342c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u32 fcs = ~(crc32_le(~0, skb->data, skb->len));
343c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
344c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* add 2 bytes for length value and 4 bytes for fcs. */
345c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	len = skb->len + 6;
346c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
347c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* The mcs7780 requires that the first two bytes are the packet
348c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * length in little endian order.  Note: the length value includes
349c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * the two bytes for the length value itself.
350c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 */
351c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[0] = len & 0xff;
352c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[1] = (len >> 8) & 0xff;
353c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* copy the data into the tx buffer. */
354d626f62b11e00c16e81e4308ab93d3f13551812aArnaldo Carvalho de Melo	skb_copy_from_linear_data(skb, buf + 2, skb->len);
355c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* put the fcs in the last four bytes in little endian order. */
356c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[len - 4] = fcs & 0xff;
357c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[len - 3] = (fcs >> 8) & 0xff;
358c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[len - 2] = (fcs >> 16) & 0xff;
359c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[len - 1] = (fcs >> 24) & 0xff;
360c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
361c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return len;
362c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
363c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
364c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Wraps the data in format for MIR */
365c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf)
366c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
367c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u16 fcs = 0;
368c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int len = skb->len + 4;
369c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
370c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len));
371c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* put the total packet length in first.  Note: packet length
372c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * value includes the two bytes that hold the packet length
373c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * itself.
374c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 */
375c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[0] = len & 0xff;
376c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[1] = (len >> 8) & 0xff;
377c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* copy the data */
378d626f62b11e00c16e81e4308ab93d3f13551812aArnaldo Carvalho de Melo	skb_copy_from_linear_data(skb, buf + 2, skb->len);
379c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* put the fcs in last two bytes in little endian order. */
380c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[len - 2] = fcs & 0xff;
381c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	buf[len - 1] = (fcs >> 8) & 0xff;
382c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
383c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return len;
384c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
385c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
386c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Unwrap received packets at MIR speed.  A 16 bit crc_ccitt checksum is
387c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * used for the fcs.  When performed over the entire packet the result
388c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * should be GOOD_FCS = 0xf0b8.  Hands the unwrapped data off to the IrDA
389c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * layer via a sk_buff.
390c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
391c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len)
392c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
393c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u16 fcs;
394c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int new_len;
395c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct sk_buff *skb;
396c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
397c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* Assume that the frames are going to fill a single packet
398c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * rather than span multiple packets.
399c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 */
400c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
401c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	new_len = len - 2;
402c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(new_len <= 0)) {
403c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		IRDA_ERROR("%s short frame length %d\n",
404c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			     mcs->netdev->name, new_len);
405af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		++mcs->netdev->stats.rx_errors;
406af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		++mcs->netdev->stats.rx_length_errors;
407c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
408c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
409c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	fcs = 0;
410c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	fcs = irda_calc_crc16(~fcs, buf, len);
411c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
412c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(fcs != GOOD_FCS) {
413c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		IRDA_ERROR("crc error calc 0x%x len %d\n",
414c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			   fcs, new_len);
415af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		mcs->netdev->stats.rx_errors++;
416af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		mcs->netdev->stats.rx_crc_errors++;
417c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
418c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
419c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
420c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb = dev_alloc_skb(new_len + 1);
421c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(!skb)) {
422af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		++mcs->netdev->stats.rx_dropped;
423c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
424c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
425c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
426c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb_reserve(skb, 1);
42727d7ff46a3498d3debc6ba68fb8014c702b81170Arnaldo Carvalho de Melo	skb_copy_to_linear_data(skb, buf, new_len);
428c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb_put(skb, new_len);
429459a98ed881802dee55897441bc7f77af614368eArnaldo Carvalho de Melo	skb_reset_mac_header(skb);
430c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb->protocol = htons(ETH_P_IRDA);
431c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb->dev = mcs->netdev;
432c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
433c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	netif_rx(skb);
434c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
435af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger	mcs->netdev->stats.rx_packets++;
436af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger	mcs->netdev->stats.rx_bytes += new_len;
437c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
438c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
439c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Unwrap received packets at FIR speed.  A 32 bit crc_ccitt checksum is
440c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * used for the fcs.  Hands the unwrapped data off to the IrDA
441c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * layer via a sk_buff.
442c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
443c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len)
444c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
445c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u32 fcs;
446c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int new_len;
447c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct sk_buff *skb;
448c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
449c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* Assume that the frames are going to fill a single packet
450c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * rather than span multiple packets.  This is most likely a false
451c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * assumption.
452c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 */
453c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
454c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	new_len = len - 4;
455c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(new_len <= 0)) {
456c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		IRDA_ERROR("%s short frame length %d\n",
457c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			   mcs->netdev->name, new_len);
458af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		++mcs->netdev->stats.rx_errors;
459af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		++mcs->netdev->stats.rx_length_errors;
460c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
461c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
462c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
463c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	fcs = ~(crc32_le(~0, buf, new_len));
4646caf52a453d5fe0bc584a2895bfd39a3d9054829Harvey Harrison	if(fcs != get_unaligned_le32(buf + new_len)) {
465c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		IRDA_ERROR("crc error calc 0x%x len %d\n", fcs, new_len);
466af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		mcs->netdev->stats.rx_errors++;
467af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		mcs->netdev->stats.rx_crc_errors++;
468c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
469c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
470c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
471c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb = dev_alloc_skb(new_len + 1);
472c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(unlikely(!skb)) {
473af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		++mcs->netdev->stats.rx_dropped;
474c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
475c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
476c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
477c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb_reserve(skb, 1);
47827d7ff46a3498d3debc6ba68fb8014c702b81170Arnaldo Carvalho de Melo	skb_copy_to_linear_data(skb, buf, new_len);
479c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb_put(skb, new_len);
480459a98ed881802dee55897441bc7f77af614368eArnaldo Carvalho de Melo	skb_reset_mac_header(skb);
481c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb->protocol = htons(ETH_P_IRDA);
482c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb->dev = mcs->netdev;
483c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
484c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	netif_rx(skb);
485c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
486af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger	mcs->netdev->stats.rx_packets++;
487af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger	mcs->netdev->stats.rx_bytes += new_len;
488c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
489c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
490c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
491c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Allocates urbs for both receive and transmit.
492c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * If alloc fails return error code 0 (fail) otherwise
493c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * return error code 1 (success).
494c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
495c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_setup_urbs(struct mcs_cb *mcs)
496c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
497c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_urb = NULL;
498c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
499c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
500c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!mcs->tx_urb)
501c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return 0;
502c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
503c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
504c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!mcs->rx_urb)
505c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return 0;
506c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
507c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return 1;
508c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
509c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
510c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Sets up state to be initially outside frame, gets receive urb,
511c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * sets status to successful and then submits the urb to start
512c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * receiving the data.
513c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
514c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_receive_start(struct mcs_cb *mcs)
515c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
516c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_buff.in_frame = FALSE;
517c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_buff.state = OUTSIDE_FRAME;
518c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
519c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev,
520c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			  usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in),
521c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			  mcs->in_buf, 4096, mcs_receive_irq, mcs);
522c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
523c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_urb->status = 0;
524c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return usb_submit_urb(mcs->rx_urb, GFP_KERNEL);
525c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
526c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
527c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Finds the in and out endpoints for the mcs control block */
528c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic inline int mcs_find_endpoints(struct mcs_cb *mcs,
529c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				     struct usb_host_endpoint *ep, int epnum)
530c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
531c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int i;
532c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
533c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
534c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* If no place to store the endpoints just return */
535c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!ep)
536c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return ret;
537c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
538c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* cycle through all endpoints, find the first two that are DIR_IN */
539c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	for (i = 0; i < epnum; i++) {
540c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if (ep[i].desc.bEndpointAddress & USB_DIR_IN)
541c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			mcs->ep_in = ep[i].desc.bEndpointAddress;
542c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		else
543c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			mcs->ep_out = ep[i].desc.bEndpointAddress;
544c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
545c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		/* MosChip says that the chip has only two bulk
546c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		 * endpoints. Find one for each direction and move on.
547c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		 */
548c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) {
549c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			ret = 1;
550c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			break;
551c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		}
552c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
553c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
554c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return ret;
555c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
556c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
557c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void mcs_speed_work(struct work_struct *work)
558c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
559c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct mcs_cb *mcs = container_of(work, struct mcs_cb, work);
560c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct net_device *netdev = mcs->netdev;
561c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
562c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs_speed_change(mcs);
563c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	netif_wake_queue(netdev);
564c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
565c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
566c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Function to change the speed of the mcs7780.  Fully supports SIR,
567c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * MIR, and FIR speeds.
568c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
569c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int mcs_speed_change(struct mcs_cb *mcs)
570c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
571c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
572c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int rst = 0;
573c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int cnt = 0;
574c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u16 nspeed;
575c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u16 rval;
576c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
577c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f];
578c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
579c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	do {
580c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		mcs_get_reg(mcs, MCS_RESV_REG, &rval);
581c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	} while(cnt++ < 100 && (rval & MCS_IRINTX));
582c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
583fb0886745a75ce98bde3aac421adc69fe61a1905Roel Kluin	if (cnt > 100) {
584599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz		IRDA_ERROR("unable to change speed\n");
585c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = -EIO;
586c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
587c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
588c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
589c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs_get_reg(mcs, MCS_MODE_REG, &rval);
590c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
59125985edcedea6396277003854657b5f3cb31a628Lucas De Marchi	/* MINRXPW values recommended by MosChip */
592c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (mcs->new_speed <= 115200) {
593c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		rval &= ~MCS_FIR;
594c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
595c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if ((rst = (mcs->speed > 115200)))
596c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			mcs_set_reg(mcs, MCS_MINRXPW_REG, 0);
597c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
598c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	} else if (mcs->new_speed <= 1152000) {
599c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		rval &= ~MCS_FIR;
600c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
601c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if ((rst = !(mcs->speed == 576000 || mcs->speed == 1152000)))
602c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			mcs_set_reg(mcs, MCS_MINRXPW_REG, 5);
603c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
604c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	} else {
605c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		rval |= MCS_FIR;
606c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
607c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if ((rst = (mcs->speed != 4000000)))
608c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			mcs_set_reg(mcs, MCS_MINRXPW_REG, 5);
609c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
610c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
611c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
612c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_SPEED_MASK;
613c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval |= nspeed;
614c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
615c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
616c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret))
617c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
618c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
619c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (rst)
620c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		switch (mcs->transceiver_type) {
621c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		case MCS_TSC_VISHAY:
622c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			ret = mcs_setup_transceiver_vishay(mcs);
623c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			break;
624c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
625c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		case MCS_TSC_SHARP:
626c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			ret = mcs_setup_transceiver_sharp(mcs);
627c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			break;
628c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
629c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		case MCS_TSC_AGILENT:
630c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			ret = mcs_setup_transceiver_agilent(mcs);
631c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			break;
632c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
633c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		default:
634c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			ret = 1;
635599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz			IRDA_WARNING("Unknown transceiver type: %d\n",
636c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				     mcs->transceiver_type);
637c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		}
638c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(ret))
639c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error;
640c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
641c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs_get_reg(mcs, MCS_MODE_REG, &rval);
642c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	rval &= ~MCS_RESET;
643c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
644c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
645c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->speed = mcs->new_speed;
646c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	error:
647c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		mcs->new_speed = 0;
648c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return ret;
649c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
650c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
651c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Ioctl calls not supported at this time.  Can be an area of future work. */
652c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
653c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
654c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* struct if_irda_req *irq = (struct if_irda_req *)rq; */
655c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* struct mcs_cb *mcs = netdev_priv(netdev); */
656c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
657c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
658c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	switch (cmd) {
659c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	default:
660c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = -EOPNOTSUPP;
661c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
662c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
663c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return ret;
664c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
665c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
666c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Network device is taken down, done by "ifconfig irda0 down" */
667c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int mcs_net_close(struct net_device *netdev)
668c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
669c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
670c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct mcs_cb *mcs = netdev_priv(netdev);
671c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
672c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* Stop transmit processing */
673c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	netif_stop_queue(netdev);
674c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
6750ff804348d66550e7dd1d6781c65403b312e2da2Hinko Kocevar	kfree_skb(mcs->rx_buff.skb);
6760ff804348d66550e7dd1d6781c65403b312e2da2Hinko Kocevar
677c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* kill and free the receive and transmit URBs */
678c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_kill_urb(mcs->rx_urb);
679c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_free_urb(mcs->rx_urb);
680c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_kill_urb(mcs->tx_urb);
681c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_free_urb(mcs->tx_urb);
682c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
683c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* Stop and remove instance of IrLAP */
684c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (mcs->irlap)
685c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		irlap_close(mcs->irlap);
686c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
687c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->irlap = NULL;
688c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return ret;
689c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
690c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
691c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Network device is taken up, done by "ifconfig irda0 up" */
692c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int mcs_net_open(struct net_device *netdev)
693c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
694c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct mcs_cb *mcs = netdev_priv(netdev);
695c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	char hwname[16];
696c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
697c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
698c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = usb_clear_halt(mcs->usbdev,
699c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			     usb_sndbulkpipe(mcs->usbdev, mcs->ep_in));
700c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (ret)
701c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error1;
702c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = usb_clear_halt(mcs->usbdev,
703c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			     usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out));
704c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (ret)
705c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error1;
706c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
707c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_setup_transceiver(mcs);
708c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (ret)
709c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error1;
710c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
711c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = -ENOMEM;
712c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
713c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* Initialize for SIR/FIR to copy data directly into skb.  */
714c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->receiving = 0;
715c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU;
716c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
717c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!mcs->rx_buff.skb)
718c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error1;
719c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
720c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	skb_reserve(mcs->rx_buff.skb, 1);
721c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->rx_buff.head = mcs->rx_buff.skb->data;
722c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	do_gettimeofday(&mcs->rx_time);
723c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
724c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/*
725c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * Now that everything should be initialized properly,
726c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * Open new IrLAP layer instance to take care of us...
727c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 * Note : will send immediately a speed change...
728c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	 */
729c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	sprintf(hwname, "usb#%d", mcs->usbdev->devnum);
730c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->irlap = irlap_open(netdev, &mcs->qos, hwname);
731c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!mcs->irlap) {
732599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz		IRDA_ERROR("mcs7780: irlap_open failed\n");
733c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error2;
734c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
735c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
736c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!mcs_setup_urbs(mcs))
7379b0f1d85968334c9bbe8675a0fc676c6e15ba577Julia Lawall		goto error3;
738c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
739c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_receive_start(mcs);
740c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (ret)
741c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error3;
742c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
743c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	netif_start_queue(netdev);
744c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return 0;
745c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
746c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	error3:
747c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		irlap_close(mcs->irlap);
748c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	error2:
749c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		kfree_skb(mcs->rx_buff.skb);
750c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	error1:
751c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return ret;
752c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
753c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
754c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* Receive callback function.  */
7557d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void mcs_receive_irq(struct urb *urb)
756c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
757c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	__u8 *bytes;
758c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct mcs_cb *mcs = urb->context;
759c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int i;
760c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret;
761c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
762c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!netif_running(mcs->netdev))
763c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
764c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
765c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (urb->status)
766c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
767c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
768c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (urb->actual_length > 0) {
769c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		bytes = urb->transfer_buffer;
770c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
771c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		/* MCS returns frames without BOF and EOF
772c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		 * I assume it returns whole frames.
773c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		 */
774c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		/* SIR speed */
775c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		if(mcs->speed < 576000) {
776af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger			async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
777c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				  &mcs->rx_buff, 0xc0);
778c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
779c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			for (i = 0; i < urb->actual_length; i++)
780af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger				async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
781c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz					  &mcs->rx_buff, bytes[i]);
782c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
783af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger			async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
784c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				  &mcs->rx_buff, 0xc1);
785c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		}
786c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		/* MIR speed */
787c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		else if(mcs->speed == 576000 || mcs->speed == 1152000) {
788c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			mcs_unwrap_mir(mcs, urb->transfer_buffer,
789c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				urb->actual_length);
790c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		}
791c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		/* FIR speed */
792c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		else {
793c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			mcs_unwrap_fir(mcs, urb->transfer_buffer,
794c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				urb->actual_length);
795c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		}
796c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		do_gettimeofday(&mcs->rx_time);
797c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
798c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
799c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = usb_submit_urb(urb, GFP_ATOMIC);
800c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
801c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
80225985edcedea6396277003854657b5f3cb31a628Lucas De Marchi/* Transmit callback function.  */
8037d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void mcs_send_irq(struct urb *urb)
804c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
805c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct mcs_cb *mcs = urb->context;
806c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct net_device *ndev = mcs->netdev;
807c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
808c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (unlikely(mcs->new_speed))
809c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		schedule_work(&mcs->work);
810c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	else
811c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		netif_wake_queue(ndev);
812c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
813c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
81425985edcedea6396277003854657b5f3cb31a628Lucas De Marchi/* Transmit callback function.  */
8156518bbb803fe02b15a3211c8db2afdff0ac4f808Stephen Hemmingerstatic netdev_tx_t mcs_hard_xmit(struct sk_buff *skb,
8166518bbb803fe02b15a3211c8db2afdff0ac4f808Stephen Hemminger				       struct net_device *ndev)
817c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
818c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	unsigned long flags;
819c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct mcs_cb *mcs;
820c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int wraplen;
821c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = 0;
822c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
823c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	netif_stop_queue(ndev);
824c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs = netdev_priv(ndev);
825c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
826c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	spin_lock_irqsave(&mcs->lock, flags);
827c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
828c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->new_speed = irda_get_next_speed(skb);
829c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (likely(mcs->new_speed == mcs->speed))
830c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		mcs->new_speed = 0;
831c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
832c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* SIR speed */
833c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if(mcs->speed < 576000) {
834c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf);
835c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
836c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* MIR speed */
837c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	else if(mcs->speed == 576000 || mcs->speed == 1152000) {
838c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf);
839c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
840c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* FIR speed */
841c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	else {
842c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf);
843c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
844c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev,
845c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			  usb_sndbulkpipe(mcs->usbdev, mcs->ep_out),
846c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			  mcs->out_buf, wraplen, mcs_send_irq, mcs);
847c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
848c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) {
849599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz		IRDA_ERROR("failed tx_urb: %d\n", ret);
850c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		switch (ret) {
851c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		case -ENODEV:
852c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		case -EPIPE:
853c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			break;
854c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		default:
855af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger			mcs->netdev->stats.tx_errors++;
856c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz			netif_start_queue(ndev);
857c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		}
858c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	} else {
859af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		mcs->netdev->stats.tx_packets++;
860af0490810cfa159b4894ddecfc5eb2e4432fb976Stephen Hemminger		mcs->netdev->stats.tx_bytes += skb->len;
861c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
862c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
863c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	dev_kfree_skb(skb);
864c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	spin_unlock_irqrestore(&mcs->lock, flags);
8654bd73ae2682d9069746bb049a416d9ab90c6684bPatrick McHardy	return NETDEV_TX_OK;
866c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
867c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
868ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemmingerstatic const struct net_device_ops mcs_netdev_ops = {
869ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemminger	.ndo_open = mcs_net_open,
870ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemminger	.ndo_stop = mcs_net_close,
871ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemminger	.ndo_start_xmit = mcs_hard_xmit,
872ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemminger	.ndo_do_ioctl = mcs_net_ioctl,
873ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemminger};
874ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemminger
875c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/*
876c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * This function is called by the USB subsystem for each new device in the
877c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz * system.  Need to verify the device and if it is, then start handling it.
878c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz */
879c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic int mcs_probe(struct usb_interface *intf,
880c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		     const struct usb_device_id *id)
881c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
882c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct usb_device *udev = interface_to_usbdev(intf);
883c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct net_device *ndev = NULL;
884c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct mcs_cb *mcs;
885c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	int ret = -ENOMEM;
886c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
887c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ndev = alloc_irdadev(sizeof(*mcs));
888c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!ndev)
889c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error1;
890c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
891599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz	IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum);
892c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
893c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	SET_NETDEV_DEV(ndev, &intf->dev);
894c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
895c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = usb_reset_configuration(udev);
896c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (ret != 0) {
897599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz		IRDA_ERROR("mcs7780: usb reset configuration failed\n");
898c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error2;
899c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
900c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
901c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs = netdev_priv(ndev);
902c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->usbdev = udev;
903c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->netdev = ndev;
904c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	spin_lock_init(&mcs->lock);
905c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
906c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* Initialize QoS for this device */
907c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	irda_init_max_qos_capabilies(&mcs->qos);
908c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
909c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* That's the Rx capability. */
910c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->qos.baud_rate.bits &=
911c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	    IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200
912c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		| IR_576000 | IR_1152000 | (IR_4000000 << 8);
913c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
914c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
915c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->qos.min_turn_time.bits &= qos_mtt_bits;
916c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	irda_qos_bits_to_value(&mcs->qos);
917c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
918c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	/* Speed change work initialisation*/
919c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&mcs->work, mcs_speed_work);
920c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
921ddc2a92d34ba20b47e1856375c68d25f51e86f53Stephen Hemminger	ndev->netdev_ops = &mcs_netdev_ops;
922c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
923c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!intf->cur_altsetting)
924c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error2;
925c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
926c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint,
927c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz				 intf->cur_altsetting->desc.bNumEndpoints);
928c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!ret) {
929c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		ret = -ENODEV;
930c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error2;
931c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	}
932c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
933c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	ret = register_netdev(ndev);
934c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (ret != 0)
935c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		goto error2;
936c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
937599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz	IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s\n",
938c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		   ndev->name);
939c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
940c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->transceiver_type = transceiver_type;
941c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->sir_tweak = sir_tweak;
942c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	mcs->receive_mode = receive_mode;
943c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
944c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_set_intfdata(intf, mcs);
945c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	return 0;
946c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
947c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	error2:
948c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		free_netdev(ndev);
949c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
950c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	error1:
951c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return ret;
952c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
953c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
954c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz/* The current device is removed, the USB layer tells us to shut down. */
955c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortizstatic void mcs_disconnect(struct usb_interface *intf)
956c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz{
957c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	struct mcs_cb *mcs = usb_get_intfdata(intf);
958c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
959c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	if (!mcs)
960c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz		return;
961c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
96223f333a2bfafba80339315b724808982a9de57d9Tejun Heo	cancel_work_sync(&mcs->work);
963c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
964c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	unregister_netdev(mcs->netdev);
965c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	free_netdev(mcs->netdev);
966c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
967c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz	usb_set_intfdata(intf, NULL);
968599b1fa91439cff8605a71f1a2b5bb42c177b667Samuel Ortiz	IRDA_DEBUG(0, "MCS7780 now disconnected.\n");
969c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz}
970c6ae522e3a50fc1ec483d7f03ece9c7a25e6de95Samuel Ortiz
971d632eb1bf22e11def74e4e53cc47d790fbdba105Greg Kroah-Hartmanmodule_usb_driver(mcs_driver);
972