nsc-ircc.c

来自「linux 内核源代码」· C语言 代码 · 共 2,406 行 · 第 1/4 页

C
2,406
字号
/********************************************************************* *                 * Filename:      nsc-ircc.c * Version:       1.0 * Description:   Driver for the NSC PC'108 and PC'338 IrDA chipsets * Status:        Stable. * Author:        Dag Brattli <dagb@cs.uit.no> * Created at:    Sat Nov  7 21:43:15 1998 * Modified at:   Wed Mar  1 11:29:34 2000 * Modified by:   Dag Brattli <dagb@cs.uit.no> *  *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> *     Copyright (c) 1998 Lichen Wang, <lwang@actisys.com> *     Copyright (c) 1998 Actisys Corp., www.actisys.com *     Copyright (c) 2000-2004 Jean Tourrilhes <jt@hpl.hp.com> *     All Rights Reserved *       *     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. *   *     Neither Dag Brattli nor University of Tromsø admit liability nor *     provide warranty for any of this software. This material is  *     provided "AS-IS" and at no charge. * *     Notice that all functions that needs to access the chip in _any_ *     way, must save BSR register on entry, and restore it on exit.  *     It is _very_ important to follow this policy! * *         __u8 bank; *      *         bank = inb(iobase+BSR); *   *         do_your_stuff_here(); * *         outb(bank, iobase+BSR); * *    If you find bugs in this file, its very likely that the same bug *    will also be in w83977af_ir.c since the implementations are quite *    similar. *      ********************************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/rtnetlink.h>#include <linux/dma-mapping.h>#include <linux/pnp.h>#include <linux/platform_device.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/byteorder.h>#include <net/irda/wrapper.h>#include <net/irda/irda.h>#include <net/irda/irda_device.h>#include "nsc-ircc.h"#define CHIP_IO_EXTENT 8#define BROKEN_DONGLE_IDstatic char *driver_name = "nsc-ircc";/* Power Management */#define NSC_IRCC_DRIVER_NAME                  "nsc-ircc"static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state);static int nsc_ircc_resume(struct platform_device *dev);static struct platform_driver nsc_ircc_driver = {	.suspend	= nsc_ircc_suspend,	.resume		= nsc_ircc_resume,	.driver		= {		.name	= NSC_IRCC_DRIVER_NAME,	},};/* Module parameters */static int qos_mtt_bits = 0x07;  /* 1 ms or more */static int dongle_id;/* Use BIOS settions by default, but user may supply module parameters */static unsigned int io[]  = { ~0, ~0, ~0, ~0, ~0 };static unsigned int irq[] = {  0,  0,  0,  0,  0 };static unsigned int dma[] = {  0,  0,  0,  0,  0 };static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id);/* These are the known NSC chips */static nsc_chip_t chips[] = {/*  Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */	{ "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, 	  nsc_ircc_probe_108, nsc_ircc_init_108 },	{ "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, 	  nsc_ircc_probe_338, nsc_ircc_init_338 },	/* Contributed by Steffen Pingel - IBM X40 */	{ "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff,	  nsc_ircc_probe_39x, nsc_ircc_init_39x },	/* Contributed by Jan Frey - IBM A30/A31 */	{ "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, 	  nsc_ircc_probe_39x, nsc_ircc_init_39x },	/* IBM ThinkPads using PC8738x (T60/X60/Z60) */	{ "IBM-PC8738x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff,	  nsc_ircc_probe_39x, nsc_ircc_init_39x },	/* IBM ThinkPads using PC8394T (T43/R52/?) */	{ "IBM-PC8394T", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf9, 0xff,	  nsc_ircc_probe_39x, nsc_ircc_init_39x },	{ NULL }};static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL };static char *dongle_types[] = {	"Differential serial interface",	"Differential serial interface",	"Reserved",	"Reserved",	"Sharp RY5HD01",	"Reserved",	"Single-ended serial interface",	"Consumer-IR only",	"HP HSDL-2300, HP HSDL-3600/HSDL-3610",	"IBM31T1100 or Temic TFDS6000/TFDS6500",	"Reserved",	"Reserved",	"HP HSDL-1100/HSDL-2100",	"HP HSDL-1100/HSDL-2100",	"Supports SIR Mode only",	"No dongle connected",};/* PNP probing */static chipio_t pnp_info;static const struct pnp_device_id nsc_ircc_pnp_table[] = {	{ .id = "NSC6001", .driver_data = 0 },	{ .id = "IBM0071", .driver_data = 0 },	{ }};MODULE_DEVICE_TABLE(pnp, nsc_ircc_pnp_table);static struct pnp_driver nsc_ircc_pnp_driver = {	.name = "nsc-ircc",	.id_table = nsc_ircc_pnp_table,	.probe = nsc_ircc_pnp_probe,};/* Some prototypes */static int  nsc_ircc_open(chipio_t *info);static int  nsc_ircc_close(struct nsc_ircc_cb *self);static int  nsc_ircc_setup(chipio_t *info);static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self);static int  nsc_ircc_dma_receive(struct nsc_ircc_cb *self); static int  nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase);static int  nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev);static int  nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev);static int  nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size);static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase);static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud);static int  nsc_ircc_is_receiving(struct nsc_ircc_cb *self);static int  nsc_ircc_read_dongle_id (int iobase);static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id);static int  nsc_ircc_net_open(struct net_device *dev);static int  nsc_ircc_net_close(struct net_device *dev);static int  nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev);/* Globals */static int pnp_registered;static int pnp_succeeded;/* * Function nsc_ircc_init () * *    Initialize chip. Just try to find out how many chips we are dealing with *    and where they are */static int __init nsc_ircc_init(void){	chipio_t info;	nsc_chip_t *chip;	int ret;	int cfg_base;	int cfg, id;	int reg;	int i = 0;	ret = platform_driver_register(&nsc_ircc_driver);        if (ret) {                IRDA_ERROR("%s, Can't register driver!\n", driver_name);                return ret;        } 	/* Register with PnP subsystem to detect disable ports */	ret = pnp_register_driver(&nsc_ircc_pnp_driver); 	if (!ret) 		pnp_registered = 1;	ret = -ENODEV;	/* Probe for all the NSC chipsets we know about */	for (chip = chips; chip->name ; chip++) {		IRDA_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__,			   chip->name);				/* Try all config registers for this chip */		for (cfg = 0; cfg < ARRAY_SIZE(chip->cfg); cfg++) {			cfg_base = chip->cfg[cfg];			if (!cfg_base)				continue;			/* Read index register */			reg = inb(cfg_base);			if (reg == 0xff) {				IRDA_DEBUG(2, "%s() no chip at 0x%03x\n", __FUNCTION__, cfg_base);				continue;			}						/* Read chip identification register */			outb(chip->cid_index, cfg_base);			id = inb(cfg_base+1);			if ((id & chip->cid_mask) == chip->cid_value) {				IRDA_DEBUG(2, "%s() Found %s chip, revision=%d\n",					   __FUNCTION__, chip->name, id & ~chip->cid_mask);				/*				 * If we found a correct PnP setting,				 * we first try it.				 */				if (pnp_succeeded) {					memset(&info, 0, sizeof(chipio_t));					info.cfg_base = cfg_base;					info.fir_base = pnp_info.fir_base;					info.dma = pnp_info.dma;					info.irq = pnp_info.irq;					if (info.fir_base < 0x2000) {						IRDA_MESSAGE("%s, chip->init\n", driver_name);						chip->init(chip, &info);					} else						chip->probe(chip, &info);					if (nsc_ircc_open(&info) >= 0)						ret = 0;				}				/*				 * Opening based on PnP values failed.				 * Let's fallback to user values, or probe				 * the chip.				 */				if (ret) {					IRDA_DEBUG(2, "%s, PnP init failed\n", driver_name);					memset(&info, 0, sizeof(chipio_t));					info.cfg_base = cfg_base;					info.fir_base = io[i];					info.dma = dma[i];					info.irq = irq[i];					/*					 * If the user supplies the base address, then					 * we init the chip, if not we probe the values					 * set by the BIOS					 */					if (io[i] < 0x2000) {						chip->init(chip, &info);					} else						chip->probe(chip, &info);					if (nsc_ircc_open(&info) >= 0)						ret = 0;				}				i++;			} else {				IRDA_DEBUG(2, "%s(), Wrong chip id=0x%02x\n", __FUNCTION__, id);			}		} 	}	if (ret) {		platform_driver_unregister(&nsc_ircc_driver);		pnp_unregister_driver(&nsc_ircc_pnp_driver);		pnp_registered = 0;	}	return ret;}/* * Function nsc_ircc_cleanup () * *    Close all configured chips * */static void __exit nsc_ircc_cleanup(void){	int i;	for (i = 0; i < ARRAY_SIZE(dev_self); i++) {		if (dev_self[i])			nsc_ircc_close(dev_self[i]);	}	platform_driver_unregister(&nsc_ircc_driver);	if (pnp_registered) 		pnp_unregister_driver(&nsc_ircc_pnp_driver);	pnp_registered = 0;}/* * Function nsc_ircc_open (iobase, irq) * *    Open driver instance * */static int __init nsc_ircc_open(chipio_t *info){	struct net_device *dev;	struct nsc_ircc_cb *self;	void *ret;	int err, chip_index;	IRDA_DEBUG(2, "%s()\n", __FUNCTION__); 	for (chip_index = 0; chip_index < ARRAY_SIZE(dev_self); chip_index++) {		if (!dev_self[chip_index])			break;	}	if (chip_index == ARRAY_SIZE(dev_self)) {		IRDA_ERROR("%s(), maximum number of supported chips reached!\n", __FUNCTION__);		return -ENOMEM;	}	IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name,		     info->cfg_base);	if ((nsc_ircc_setup(info)) == -1)		return -1;	IRDA_MESSAGE("%s, driver loaded (Dag Brattli)\n", driver_name);	dev = alloc_irdadev(sizeof(struct nsc_ircc_cb));	if (dev == NULL) {		IRDA_ERROR("%s(), can't allocate memory for "			   "control block!\n", __FUNCTION__);		return -ENOMEM;	}	self = dev->priv;	self->netdev = dev;	spin_lock_init(&self->lock);   	/* Need to store self somewhere */	dev_self[chip_index] = self;	self->index = chip_index;	/* Initialize IO */	self->io.cfg_base  = info->cfg_base;	self->io.fir_base  = info->fir_base;        self->io.irq       = info->irq;        self->io.fir_ext   = CHIP_IO_EXTENT;        self->io.dma       = info->dma;        self->io.fifo_size = 32;		/* Reserve the ioports that we need */	ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);	if (!ret) {		IRDA_WARNING("%s(), can't get iobase of 0x%03x\n",			     __FUNCTION__, self->io.fir_base);		err = -ENODEV;		goto out1;	}	/* Initialize QoS for this device */	irda_init_max_qos_capabilies(&self->qos);		/* The only value we must override it the baudrate */	self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|		IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8);		self->qos.min_turn_time.bits = qos_mtt_bits;	irda_qos_bits_to_value(&self->qos);		/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */	self->rx_buff.truesize = 14384; 	self->tx_buff.truesize = 14384;	/* Allocate memory if needed */	self->rx_buff.head =		dma_alloc_coherent(NULL, self->rx_buff.truesize,				   &self->rx_buff_dma, GFP_KERNEL);	if (self->rx_buff.head == NULL) {		err = -ENOMEM;		goto out2;	}	memset(self->rx_buff.head, 0, self->rx_buff.truesize);		self->tx_buff.head =		dma_alloc_coherent(NULL, self->tx_buff.truesize,				   &self->tx_buff_dma, GFP_KERNEL);	if (self->tx_buff.head == NULL) {		err = -ENOMEM;		goto out3;	}	memset(self->tx_buff.head, 0, self->tx_buff.truesize);	self->rx_buff.in_frame = FALSE;	self->rx_buff.state = OUTSIDE_FRAME;	self->tx_buff.data = self->tx_buff.head;	self->rx_buff.data = self->rx_buff.head;		/* Reset Tx queue info */	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;	self->tx_fifo.tail = self->tx_buff.head;	/* Override the network functions we need to use */	dev->hard_start_xmit = nsc_ircc_hard_xmit_sir;	dev->open            = nsc_ircc_net_open;	dev->stop            = nsc_ircc_net_close;	dev->do_ioctl        = nsc_ircc_net_ioctl;	dev->get_stats	     = nsc_ircc_net_get_stats;	err = register_netdev(dev);	if (err) {		IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);		goto out4;	}	IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name);	/* Check if user has supplied a valid dongle id or not */	if ((dongle_id <= 0) ||	    (dongle_id >= ARRAY_SIZE(dongle_types))) {		dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base);				IRDA_MESSAGE("%s, Found dongle: %s\n", driver_name,			     dongle_types[dongle_id]);	} else {		IRDA_MESSAGE("%s, Using dongle: %s\n", driver_name,			     dongle_types[dongle_id]);	}		self->io.dongle_id = dongle_id;	nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); 	self->pldev = platform_device_register_simple(NSC_IRCC_DRIVER_NAME, 						      self->index, NULL, 0); 	if (IS_ERR(self->pldev)) { 		err = PTR_ERR(self->pldev); 		goto out5; 	} 	platform_set_drvdata(self->pldev, self);	return chip_index; out5: 	unregister_netdev(dev); out4:	dma_free_coherent(NULL, self->tx_buff.truesize,			  self->tx_buff.head, self->tx_buff_dma); out3:	dma_free_coherent(NULL, self->rx_buff.truesize,			  self->rx_buff.head, self->rx_buff_dma); out2:	release_region(self->io.fir_base, self->io.fir_ext); out1:	free_netdev(dev);	dev_self[chip_index] = NULL;	return err;}/* * Function nsc_ircc_close (self) * *    Close driver instance * */static int __exit nsc_ircc_close(struct nsc_ircc_cb *self){	int iobase;	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);	IRDA_ASSERT(self != NULL, return -1;);        iobase = self->io.fir_base;	platform_device_unregister(self->pldev);	/* Remove netdevice */	unregister_netdev(self->netdev);	/* Release the PORT that this driver is using */	IRDA_DEBUG(4, "%s(), Releasing Region %03x\n", 		   __FUNCTION__, self->io.fir_base);	release_region(self->io.fir_base, self->io.fir_ext);	if (self->tx_buff.head)		dma_free_coherent(NULL, self->tx_buff.truesize,				  self->tx_buff.head, self->tx_buff_dma);		if (self->rx_buff.head)		dma_free_coherent(NULL, self->rx_buff.truesize,				  self->rx_buff.head, self->rx_buff_dma);	dev_self[self->index] = NULL;	free_netdev(self->netdev);		return 0;}/* * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) * *    Initialize the NSC '108 chip * */static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info){	int cfg_base = info->cfg_base;	__u8 temp=0;	outb(2, cfg_base);      /* Mode Control Register (MCTL) */	outb(0x00, cfg_base+1); /* Disable device */		/* Base Address and Interrupt Control Register (BAIC) */	outb(CFG_108_BAIC, cfg_base);	switch (info->fir_base) {	case 0x3e8: outb(0x14, cfg_base+1); break;	case 0x2e8: outb(0x15, cfg_base+1); break;	case 0x3f8: outb(0x16, cfg_base+1); break;	case 0x2f8: outb(0x17, cfg_base+1); break;	default: IRDA_ERROR("%s(), invalid base_address", __FUNCTION__);	}		/* Control Signal Routing Register (CSRT) */	switch (info->irq) {	case 3:  temp = 0x01; break;	case 4:  temp = 0x02; break;	case 5:  temp = 0x03; break;	case 7:  temp = 0x04; break;	case 9:  temp = 0x05; break;	case 11: temp = 0x06; break;	case 15: temp = 0x07; break;	default: IRDA_ERROR("%s(), invalid irq", __FUNCTION__);	}	outb(CFG_108_CSRT, cfg_base);		switch (info->dma) {		case 0: outb(0x08+temp, cfg_base+1); break;	case 1: outb(0x10+temp, cfg_base+1); break;	case 3: outb(0x18+temp, cfg_base+1); break;	default: IRDA_ERROR("%s(), invalid dma", __FUNCTION__);	}		outb(CFG_108_MCTL, cfg_base);      /* Mode Control Register (MCTL) */	outb(0x03, cfg_base+1); /* Enable device */	return 0;}/* * Function nsc_ircc_probe_108 (chip, info) * *     * */static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) {	int cfg_base = info->cfg_base;	int reg;	/* Read address and interrupt control register (BAIC) */	outb(CFG_108_BAIC, cfg_base);	reg = inb(cfg_base+1);		switch (reg & 0x03) {	case 0:		info->fir_base = 0x3e8;		break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?