⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mcs7780.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/******************************************************************************* 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 + -