📄 mcs7780.c
字号:
/******************************************************************************* Filename: mcs7780.c* Version: 0.4-alpha* Description: Irda MosChip USB Dongle Driver* Authors: Lukasz Stelmach <stlman@poczta.fm>* Brian Pugh <bpugh@cs.pdx.edu>* Judy Fischbach <jfisch@cs.pdx.edu>** Based on stir4200 driver, but some things done differently.* Based on earlier driver by Paul Stewart <stewart@parc.com>** Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>* Copyright (C) 2001, Dag Brattli <dag@brattli.net>* Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>* Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>* Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm>* Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu>* Copyright (C) 2005, Judy Fischbach <jfisch@cs.pdx.edu>** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.******************************************************************************//* * MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither * compatibile with irda-usb nor with stir4200. Although it is quite * similar to the later as far as general idea of operation is concerned. * That is it requires the software to do all the framing job at SIR speeds. * The hardware does take care of the framing at MIR and FIR speeds. * It supports all speeds from 2400 through 4Mbps */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/kref.h>#include <linux/usb.h>#include <linux/device.h>#include <linux/crc32.h>#include <asm/unaligned.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#include <net/irda/irda.h>#include <net/irda/wrapper.h>#include <net/irda/crc.h>#include "mcs7780.h"#define MCS_VENDOR_ID 0x9710#define MCS_PRODUCT_ID 0x7780static struct usb_device_id mcs_table[] = { /* MosChip Corp., MCS7780 FIR-USB Adapter */ {USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)}, {},};MODULE_AUTHOR("Brian Pugh <bpugh@cs.pdx.edu>");MODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780");MODULE_VERSION("0.3alpha");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(usb, mcs_table);static int qos_mtt_bits = 0x07 /* > 1ms */ ;module_param(qos_mtt_bits, int, 0);MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");static int receive_mode = 0x1;module_param(receive_mode, int, 0);MODULE_PARM_DESC(receive_mode, "Receive mode of the device (1:fast, 0:slow, default:1)");static int sir_tweak = 1;module_param(sir_tweak, int, 0444);MODULE_PARM_DESC(sir_tweak, "Default pulse width (1:1.6us, 0:3/16 bit, default:1).");static int transceiver_type = MCS_TSC_VISHAY;module_param(transceiver_type, int, 0444);MODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h.");static struct usb_driver mcs_driver = { .name = "mcs7780", .probe = mcs_probe, .disconnect = mcs_disconnect, .id_table = mcs_table,};/* speed flag selection by direct addressing.addr = (speed >> 8) & 0x0f0x1 57600 0x2 115200 0x4 1152000 0x5 96000x6 38400 0x9 2400 0xa 576000 0xb 192004Mbps (or 2400) must be checked separately. Since it also hasto be programmed in a different manner that is not a big problem.*/static __u16 mcs_speed_set[16] = { 0, MCS_SPEED_57600, MCS_SPEED_115200, 0, MCS_SPEED_1152000, MCS_SPEED_9600, MCS_SPEED_38400, 0, 0, MCS_SPEED_2400, MCS_SPEED_576000, MCS_SPEED_19200, 0, 0, 0,};/* Set given 16 bit register with a 16 bit value. Send control message * to set dongle register. */static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val){ struct usb_device *dev = mcs->usbdev; return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE, val, reg, NULL, 0, msecs_to_jiffies(MCS_CTRL_TIMEOUT));}/* Get 16 bit register value. Send contol message to read dongle register. */static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val){ struct usb_device *dev = mcs->usbdev; int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, 0, reg, val, 2, msecs_to_jiffies(MCS_CTRL_TIMEOUT)); return ret;}/* Setup a communication between mcs7780 and TFDU chips. It is described * in more detail in the data sheet. The setup sequence puts the the * vishay tranceiver into high speed mode. It will also receive SIR speed * packets but at reduced sensitivity. *//* 0: OK 1:ERROR */static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs){ int ret = 0; __u16 rval; /* mcs_get_reg should read exactly two bytes from the dongle */ ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); if (unlikely(ret != 2)) { ret = -EIO; goto error; } /* The MCS_XCVR_CONF bit puts the transceiver into configuration * mode. The MCS_MODE0 bit must start out high (1) and then * transition to low and the MCS_STFIR and MCS_MODE1 bits must * be low. */ rval |= (MCS_MODE0 | MCS_XCVR_CONF); rval &= ~MCS_STFIR; rval &= ~MCS_MODE1; ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); if (unlikely(ret)) goto error; rval &= ~MCS_MODE0; ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); if (unlikely(ret)) goto error; rval &= ~MCS_XCVR_CONF; ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); if (unlikely(ret)) goto error; ret = 0; error: return ret;}/* Setup a communication between mcs7780 and agilent chip. */static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs){ IRDA_WARNING("This transceiver type is not supported yet."); return 1;}/* Setup a communication between mcs7780 and sharp chip. */static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs){ IRDA_WARNING("This transceiver type is not supported yet."); return 1;}/* Common setup for all transceivers */static inline int mcs_setup_transceiver(struct mcs_cb *mcs){ int ret = 0; __u16 rval; char *msg; msg = "Basic transceiver setup error."; /* read value of MODE Register, set the DRIVER and RESET bits * and write value back out to MODE Register */ ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); if(unlikely(ret != 2)) goto error; rval |= MCS_DRIVER; /* put the mcs7780 into configuration mode. */ ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); if(unlikely(ret)) goto error; rval = 0; /* set min pulse width to 0 initially. */ ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval); if(unlikely(ret)) goto error; ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); if(unlikely(ret != 2)) goto error; rval &= ~MCS_FIR; /* turn off fir mode. */ if(mcs->sir_tweak) rval |= MCS_SIR16US; /* 1.6us pulse width */ else rval &= ~MCS_SIR16US; /* 3/16 bit time pulse width */ /* make sure ask mode and back to back packets are off. */ rval &= ~(MCS_BBTG | MCS_ASK); rval &= ~MCS_SPEED_MASK; rval |= MCS_SPEED_9600; /* make sure initial speed is 9600. */ mcs->speed = 9600; mcs->new_speed = 0; /* new_speed is set to 0 */ rval &= ~MCS_PLLPWDN; /* disable power down. */ /* make sure device determines direction and that the auto send sip * pulse are on. */ rval |= MCS_DTD | MCS_SIPEN; ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); if(unlikely(ret)) goto error; msg = "transceiver model specific setup error."; switch (mcs->transceiver_type) { case MCS_TSC_VISHAY: ret = mcs_setup_transceiver_vishay(mcs); break; case MCS_TSC_SHARP: ret = mcs_setup_transceiver_sharp(mcs); break; case MCS_TSC_AGILENT: ret = mcs_setup_transceiver_agilent(mcs); break; default: IRDA_WARNING("Unknown transceiver type: %d", mcs->transceiver_type); ret = 1; } if (unlikely(ret)) goto error; /* If transceiver is not SHARP, then if receive mode set * on the RXFAST bit in the XCVR Register otherwise unset it */ if (mcs->transceiver_type != MCS_TSC_SHARP) { ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); if (unlikely(ret != 2)) goto error; if (mcs->receive_mode) rval |= MCS_RXFAST; else rval &= ~MCS_RXFAST; ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); if (unlikely(ret)) goto error; } msg = "transceiver reset."; ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); if (unlikely(ret != 2)) goto error; /* reset the mcs7780 so all changes take effect. */ rval &= ~MCS_RESET; ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); if (unlikely(ret)) goto error; else return ret;error: IRDA_ERROR("%s", msg); return ret;}/* Wraps the data in format for SIR */static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf){ int wraplen; /* 2: full frame length, including "the length" */ wraplen = async_wrap_skb(skb, buf + 2, 4094); wraplen += 2; buf[0] = wraplen & 0xff; buf[1] = (wraplen >> 8) & 0xff; return wraplen;}/* Wraps the data in format for FIR */static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf){ unsigned int len = 0; __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); /* add 2 bytes for length value and 4 bytes for fcs. */ len = skb->len + 6; /* The mcs7780 requires that the first two bytes are the packet * length in little endian order. Note: the length value includes * the two bytes for the length value itself. */ buf[0] = len & 0xff; buf[1] = (len >> 8) & 0xff; /* copy the data into the tx buffer. */ memcpy(buf+2, skb->data, skb->len); /* put the fcs in the last four bytes in little endian order. */ buf[len - 4] = fcs & 0xff; buf[len - 3] = (fcs >> 8) & 0xff; buf[len - 2] = (fcs >> 16) & 0xff; buf[len - 1] = (fcs >> 24) & 0xff; return len;}/* Wraps the data in format for MIR */static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf){ __u16 fcs = 0; int len = skb->len + 4; fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len)); /* put the total packet length in first. Note: packet length * value includes the two bytes that hold the packet length * itself. */ buf[0] = len & 0xff; buf[1] = (len >> 8) & 0xff; /* copy the data */ memcpy(buf+2, skb->data, skb->len); /* put the fcs in last two bytes in little endian order. */ buf[len - 2] = fcs & 0xff; buf[len - 1] = (fcs >> 8) & 0xff; return len;}/* Unwrap received packets at MIR speed. A 16 bit crc_ccitt checksum is * used for the fcs. When performed over the entire packet the result * should be GOOD_FCS = 0xf0b8. Hands the unwrapped data off to the IrDA * layer via a sk_buff. */static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len){ __u16 fcs; int new_len; struct sk_buff *skb; /* Assume that the frames are going to fill a single packet * rather than span multiple packets. */ new_len = len - 2; if(unlikely(new_len <= 0)) { IRDA_ERROR("%s short frame length %d\n", mcs->netdev->name, new_len); ++mcs->stats.rx_errors; ++mcs->stats.rx_length_errors; return; } fcs = 0; fcs = irda_calc_crc16(~fcs, buf, len); if(fcs != GOOD_FCS) { IRDA_ERROR("crc error calc 0x%x len %d\n", fcs, new_len); mcs->stats.rx_errors++; mcs->stats.rx_crc_errors++; return; } skb = dev_alloc_skb(new_len + 1); if(unlikely(!skb)) { ++mcs->stats.rx_dropped; return; } skb_reserve(skb, 1); memcpy(skb->data, buf, new_len); skb_put(skb, new_len); skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); skb->dev = mcs->netdev; netif_rx(skb); mcs->stats.rx_packets++; mcs->stats.rx_bytes += new_len; return;}/* Unwrap received packets at FIR speed. A 32 bit crc_ccitt checksum is * used for the fcs. Hands the unwrapped data off to the IrDA * layer via a sk_buff. */static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len){ __u32 fcs; int new_len; struct sk_buff *skb; /* Assume that the frames are going to fill a single packet * rather than span multiple packets. This is most likely a false * assumption. */ new_len = len - 4; if(unlikely(new_len <= 0)) { IRDA_ERROR("%s short frame length %d\n", mcs->netdev->name, new_len); ++mcs->stats.rx_errors; ++mcs->stats.rx_length_errors; return; } fcs = ~(crc32_le(~0, buf, new_len)); if(fcs != le32_to_cpu(get_unaligned((u32 *)(buf+new_len)))) { IRDA_ERROR("crc error calc 0x%x len %d\n", fcs, new_len); mcs->stats.rx_errors++; mcs->stats.rx_crc_errors++; return; } skb = dev_alloc_skb(new_len + 1); if(unlikely(!skb)) { ++mcs->stats.rx_dropped; return; } skb_reserve(skb, 1); memcpy(skb->data, buf, new_len); skb_put(skb, new_len); skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); skb->dev = mcs->netdev; netif_rx(skb); mcs->stats.rx_packets++; mcs->stats.rx_bytes += new_len; return;}/* Allocates urbs for both receive and transmit. * If alloc fails return error code 0 (fail) otherwise * return error code 1 (success). */static inline int mcs_setup_urbs(struct mcs_cb *mcs){ mcs->rx_urb = NULL; mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -