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

📄 cosa.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	 * The following loop is indexed through i (instead of id)	 * to avoid looping forever when for any reason	 * the port returns '\r', '\n' or '\x2e' permanently.	 */	for (i=0; i<COSA_MAX_ID_STRING-1; i++, prev=curr) {		if ((curr = get_wait_data(cosa)) == -1) {			return -1;		}		curr &= 0xff;		if (curr != '\r' && curr != '\n' && curr != 0x2e)			idstring[id++] = curr;		if (curr == 0x2e && prev == '\n')			break;	}	/* Perhaps we should fail when i==COSA_MAX_ID_STRING-1 ? */	idstring[id] = '\0';	return id;}/* ---------- Auxiliary routines for COSA/SRP monitor ---------- *//* * This routine gets the data byte from the card waiting for the SR_RX_RDY * bit to be set in a loop. It should be used in the exceptional cases * only (for example when resetting the card or downloading the firmware. */static int get_wait_data(struct cosa_data *cosa){	int retries = 1000;	while (--retries) {		/* read data and return them */		if (cosa_getstatus(cosa) & SR_RX_RDY) {			short r;			r = cosa_getdata8(cosa);#if 0			printk(KERN_INFO "cosa: get_wait_data returning after %d retries\n", 999-retries);#endif			return r;		}		/* sleep if not ready to read */		current->state = TASK_INTERRUPTIBLE;		schedule_timeout(1);	}	printk(KERN_INFO "cosa: timeout in get_wait_data (status 0x%x)\n",		cosa_getstatus(cosa));	return -1;}/* * This routine puts the data byte to the card waiting for the SR_TX_RDY * bit to be set in a loop. It should be used in the exceptional cases * only (for example when resetting the card or downloading the firmware). */static int put_wait_data(struct cosa_data *cosa, int data){	int retries = 1000;	while (--retries) {		/* read data and return them */		if (cosa_getstatus(cosa) & SR_TX_RDY) {			cosa_putdata8(cosa, data);#if 0			printk(KERN_INFO "Putdata: %d retries\n", 999-retries);#endif			return 0;		}#if 0		/* sleep if not ready to read */		current->state = TASK_INTERRUPTIBLE;		schedule_timeout(1);#endif	}	printk(KERN_INFO "cosa%d: timeout in put_wait_data (status 0x%x)\n",		cosa->num, cosa_getstatus(cosa));	return -1;}	/*  * The following routine puts the hexadecimal number into the SRP monitor * and verifies the proper echo of the sent bytes. Returns 0 on success, * negative number on failure (-1,-3,-5,-7) means that put_wait_data() failed, * (-2,-4,-6,-8) means that reading echo failed. */static int puthexnumber(struct cosa_data *cosa, int number){	char temp[5];	int i;	/* Well, I should probably replace this by something faster. */	sprintf(temp, "%04X", number);	for (i=0; i<4; i++) {		if (put_wait_data(cosa, temp[i]) == -1) {			printk(KERN_NOTICE "cosa%d: puthexnumber failed to write byte %d\n",				cosa->num, i);			return -1-2*i;		}		if (get_wait_data(cosa) != temp[i]) {			printk(KERN_NOTICE "cosa%d: puthexhumber failed to read echo of byte %d\n",				cosa->num, i);			return -2-2*i;		}	}	return 0;}/* ---------- Interrupt routines ---------- *//* * There are three types of interrupt: * At the beginning of transmit - this handled is in tx_interrupt(), * at the beginning of receive - it is in rx_interrupt() and * at the end of transmit/receive - it is the eot_interrupt() function. * These functions are multiplexed by cosa_interrupt() according to the * COSA status byte. I have moved the rx/tx/eot interrupt handling into * separate functions to make it more readable. These functions are inline, * so there should be no overhead of function call. *  * In the COSA bus-master mode, we need to tell the card the address of a * buffer. Unfortunately, COSA may be too slow for us, so we must busy-wait. * It's time to use the bottom half :-( *//* * Transmit interrupt routine - called when COSA is willing to obtain * data from the OS. The most tricky part of the routine is selection * of channel we (OS) want to send packet for. For SRP we should probably * use the round-robin approach. The newer COSA firmwares have a simple * flow-control - in the status word has bits 2 and 3 set to 1 means that the * channel 0 or 1 doesn't want to receive data. * * It seems there is a bug in COSA firmware (need to trace it further): * When the driver status says that the kernel has no more data for transmit * (e.g. at the end of TX DMA) and then the kernel changes its mind * (e.g. new packet is queued to hard_start_xmit()), the card issues * the TX interrupt but does not mark the channel as ready-to-transmit. * The fix seems to be to push the packet to COSA despite its request. * We first try to obey the card's opinion, and then fall back to forced TX. */static inline void tx_interrupt(struct cosa_data *cosa, int status){	unsigned long flags, flags1;#ifdef DEBUG_IRQS	printk(KERN_INFO "cosa%d: SR_DOWN_REQUEST status=0x%04x\n",		cosa->num, status);#endif	spin_lock_irqsave(&cosa->lock, flags);	set_bit(TXBIT, &cosa->rxtx);	if (!test_bit(IRQBIT, &cosa->rxtx)) {		/* flow control, see the comment above */		int i=0;		if (!cosa->txbitmap) {			printk(KERN_WARNING "%s: No channel wants data "				"in TX IRQ. Expect DMA timeout.",				cosa->name);			put_driver_status_nolock(cosa);			clear_bit(TXBIT, &cosa->rxtx);			spin_unlock_irqrestore(&cosa->lock, flags);			return;		}		while(1) {			cosa->txchan++;			i++;			if (cosa->txchan >= cosa->nchannels)				cosa->txchan = 0;			if (!(cosa->txbitmap & (1<<cosa->txchan)))				continue;			if (~status & (1 << (cosa->txchan+DRIVER_TXMAP_SHIFT)))				break;			/* in second pass, accept first ready-to-TX channel */			if (i > cosa->nchannels) {				/* Can be safely ignored */#ifdef DEBUG_IRQS				printk(KERN_DEBUG "%s: Forcing TX "					"to not-ready channel %d\n",					cosa->name, cosa->txchan);#endif				break;			}		}		cosa->txsize = cosa->chan[cosa->txchan].txsize;		if (cosa_dma_able(cosa->chan+cosa->txchan,			cosa->chan[cosa->txchan].txbuf, cosa->txsize)) {			cosa->txbuf = cosa->chan[cosa->txchan].txbuf;		} else {			memcpy(cosa->bouncebuf, cosa->chan[cosa->txchan].txbuf,				cosa->txsize);			cosa->txbuf = cosa->bouncebuf;		}	}	if (is_8bit(cosa)) {		if (!test_bit(IRQBIT, &cosa->rxtx)) {			cosa_putstatus(cosa, SR_TX_INT_ENA);			cosa_putdata8(cosa, ((cosa->txchan << 5) & 0xe0)|				((cosa->txsize >> 8) & 0x1f));#ifdef DEBUG_IO			debug_status_out(cosa, SR_TX_INT_ENA);			debug_data_out(cosa, ((cosa->txchan << 5) & 0xe0)|                                ((cosa->txsize >> 8) & 0x1f));			debug_data_in(cosa, cosa_getdata8(cosa));#else			cosa_getdata8(cosa);#endif			set_bit(IRQBIT, &cosa->rxtx);			spin_unlock_irqrestore(&cosa->lock, flags);			return;		} else {			clear_bit(IRQBIT, &cosa->rxtx);			cosa_putstatus(cosa, 0);			cosa_putdata8(cosa, cosa->txsize&0xff);#ifdef DEBUG_IO			debug_status_out(cosa, 0);			debug_data_out(cosa, cosa->txsize&0xff);#endif		}	} else {		cosa_putstatus(cosa, SR_TX_INT_ENA);		cosa_putdata16(cosa, ((cosa->txchan<<13) & 0xe000)			| (cosa->txsize & 0x1fff));#ifdef DEBUG_IO		debug_status_out(cosa, SR_TX_INT_ENA);		debug_data_out(cosa, ((cosa->txchan<<13) & 0xe000)                        | (cosa->txsize & 0x1fff));		debug_data_in(cosa, cosa_getdata8(cosa));		debug_status_out(cosa, 0);#else		cosa_getdata8(cosa);#endif		cosa_putstatus(cosa, 0);	}	if (cosa->busmaster) {		unsigned long addr = virt_to_bus(cosa->txbuf);		int count=0;		printk(KERN_INFO "busmaster IRQ\n");		while (!(cosa_getstatus(cosa)&SR_TX_RDY)) {			count++;			udelay(10);			if (count > 1000) break;		}		printk(KERN_INFO "status %x\n", cosa_getstatus(cosa));		printk(KERN_INFO "ready after %d loops\n", count);		cosa_putdata16(cosa, (addr >> 16)&0xffff);		count = 0;		while (!(cosa_getstatus(cosa)&SR_TX_RDY)) {			count++;			if (count > 1000) break;			udelay(10);		}		printk(KERN_INFO "ready after %d loops\n", count);		cosa_putdata16(cosa, addr &0xffff);		flags1 = claim_dma_lock();		set_dma_mode(cosa->dma, DMA_MODE_CASCADE);		enable_dma(cosa->dma);		release_dma_lock(flags1);	} else {		/* start the DMA */		flags1 = claim_dma_lock();		disable_dma(cosa->dma);		clear_dma_ff(cosa->dma);		set_dma_mode(cosa->dma, DMA_MODE_WRITE);		set_dma_addr(cosa->dma, virt_to_bus(cosa->txbuf));		set_dma_count(cosa->dma, cosa->txsize);		enable_dma(cosa->dma);		release_dma_lock(flags1);	}	cosa_putstatus(cosa, SR_TX_DMA_ENA|SR_USR_INT_ENA);#ifdef DEBUG_IO	debug_status_out(cosa, SR_TX_DMA_ENA|SR_USR_INT_ENA);#endif	spin_unlock_irqrestore(&cosa->lock, flags);}static inline void rx_interrupt(struct cosa_data *cosa, int status){	unsigned long flags;#ifdef DEBUG_IRQS	printk(KERN_INFO "cosa%d: SR_UP_REQUEST\n", cosa->num);#endif	spin_lock_irqsave(&cosa->lock, flags);	set_bit(RXBIT, &cosa->rxtx);	if (is_8bit(cosa)) {		if (!test_bit(IRQBIT, &cosa->rxtx)) {			set_bit(IRQBIT, &cosa->rxtx);			put_driver_status_nolock(cosa);			cosa->rxsize = cosa_getdata8(cosa) <<8;#ifdef DEBUG_IO			debug_data_in(cosa, cosa->rxsize >> 8);#endif			spin_unlock_irqrestore(&cosa->lock, flags);			return;		} else {			clear_bit(IRQBIT, &cosa->rxtx);			cosa->rxsize |= cosa_getdata8(cosa) & 0xff;#ifdef DEBUG_IO			debug_data_in(cosa, cosa->rxsize & 0xff);#endif#if 0			printk(KERN_INFO "cosa%d: receive rxsize = (0x%04x).\n",				cosa->num, cosa->rxsize);#endif		}	} else {		cosa->rxsize = cosa_getdata16(cosa);#ifdef DEBUG_IO		debug_data_in(cosa, cosa->rxsize);#endif#if 0		printk(KERN_INFO "cosa%d: receive rxsize = (0x%04x).\n",			cosa->num, cosa->rxsize);#endif	}	if (((cosa->rxsize & 0xe000) >> 13) >= cosa->nchannels) {		printk(KERN_WARNING "%s: rx for unknown channel (0x%04x)\n",			cosa->name, cosa->rxsize);		spin_unlock_irqrestore(&cosa->lock, flags);		goto reject;	}	cosa->rxchan = cosa->chan + ((cosa->rxsize & 0xe000) >> 13);	cosa->rxsize &= 0x1fff;	spin_unlock_irqrestore(&cosa->lock, flags);	cosa->rxbuf = NULL;	if (cosa->rxchan->setup_rx)		cosa->rxbuf = cosa->rxchan->setup_rx(cosa->rxchan, cosa->rxsize);	if (!cosa->rxbuf) {reject:		/* Reject the packet */		printk(KERN_INFO "cosa%d: rejecting packet on channel %d\n",			cosa->num, cosa->rxchan->num);		cosa->rxbuf = cosa->bouncebuf;	}	/* start the DMA */	flags = claim_dma_lock();	disable_dma(cosa->dma);	clear_dma_ff(cosa->dma);	set_dma_mode(cosa->dma, DMA_MODE_READ);	if (cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize & 0x1fff)) {		set_dma_addr(cosa->dma, virt_to_bus(cosa->rxbuf));	} else {		set_dma_addr(cosa->dma, virt_to_bus(cosa->bouncebuf));	}	set_dma_count(cosa->dma, (cosa->rxsize&0x1fff));	enable_dma(cosa->dma);	release_dma_lock(flags);	spin_lock_irqsave(&cosa->lock, flags);	cosa_putstatus(cosa, SR_RX_DMA_ENA|SR_USR_INT_ENA);	if (!is_8bit(cosa) && (status & SR_TX_RDY))		cosa_putdata8(cosa, DRIVER_RX_READY);#ifdef DEBUG_IO	debug_status_out(cosa, SR_RX_DMA_ENA|SR_USR_INT_ENA);	if (!is_8bit(cosa) && (status & SR_TX_RDY))		debug_data_cmd(cosa, DRIVER_RX_READY);#endif	spin_unlock_irqrestore(&cosa->lock, flags);}static inline void eot_interrupt(struct cosa_data *cosa, int status){	unsigned long flags, flags1;	spin_lock_irqsave(&cosa->lock, flags);	flags1 = claim_dma_lock();	disable_dma(cosa->dma);	clear_dma_ff(cosa->dma);	release_dma_lock(flags1);	if (test_bit(TXBIT, &cosa->rxtx)) {		struct channel_data *chan = cosa->chan+cosa->txchan;		if (chan->tx_done)			if (chan->tx_done(chan, cosa->txsize))				clear_bit(chan->num, &cosa->txbitmap);	} else if (test_bit(RXBIT, &cosa->rxtx)) {#ifdef DEBUG_DATA	{		int i;		printk(KERN_INFO "cosa%dc%d: done rx(0x%x)", cosa->num, 			cosa->rxchan->num, cosa->rxsize);		for (i=0; i<cosa->rxsize; i++)			printk (" %02x", cosa->rxbuf[i]&0xff);		printk("\n");	}#endif		/* Packet for unknown channel? */		if (cosa->rxbuf == cosa->bouncebuf)			goto out;		if (!cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize))			memcpy(cosa->rxbuf, cosa->bouncebuf, cosa->rxsize);		if (cosa->rxchan->rx_done)			if (cosa->rxchan->rx_done(cosa->rxchan))				clear_bit(cosa->rxchan->num, &cosa->rxbitmap);	} else {		printk(KERN_NOTICE "cosa%d: unexpected EOT interrupt\n",			cosa->num);	}	/*	 * Clear the RXBIT, TXBIT and IRQBIT (the latest should be	 * cleared anyway). We should do it as soon as possible	 * so that we can tell the COSA we are done and to give it a time	 * for recovery.	 */out:	cosa->rxtx = 0;	put_driver_status_nolock(cosa);	spin_unlock_irqrestore(&cosa->lock, flags);}static irqreturn_t cosa_interrupt(int irq, void *cosa_, struct pt_regs *regs){	unsigned status;	int count = 0;	struct cosa_data *cosa = cosa_;again:	status = cosa_getstatus(cosa);#ifdef DEBUG_IRQS	printk(KERN_INFO "cosa%d: got IRQ, status 0x%02x\n", cosa->num,		status & 0xff);#endif#ifdef DEBUG_IO	debug_status_in(cosa, status);#endif	switch (status & SR_CMD_FROM_SRP_MASK) {	case SR_DOWN_REQUEST:		tx_interrupt(cosa, status);		break;	case SR_UP_REQUEST:		rx_interrupt(cosa, status);		break;	case SR_END_OF_TRANSFER:		eot_interrupt(cosa, status);		break;	default:		/* We may be too fast for SRP. Try to wait a bit more. */		if (count++ < 100) {			udelay(100);			goto again;		}		printk(KERN_INFO "cosa%d: unknown status 0x%02x in IRQ after %d retries\n",			cosa->num, status & 0xff, count);	}#ifdef DEBUG_IRQS	if (count)		printk(KERN_INFO "%s: %d-times got unknown status in IRQ\n",			cosa->name, count);	else		printk(KERN_INFO "%s: returning from IRQ\n", cosa->name);#endif	return IRQ_HANDLED;}/* ---------- I/O debugging routines ---------- *//* * These routines can be used to monitor COSA/SRP I/O and to printk() * the data being transferred on the data and status I/O port in a * readable way. */#ifdef DEBUG_IOstatic void debug_status_in(struct cosa_data *cosa, int status){	char *s;	switch(status & SR_CMD_FROM_SRP_MASK) {	case SR_UP_REQUEST:		s = "RX_REQ";		break;	case SR_DOWN_REQUEST:		s = "TX_REQ";		break;	case SR_END_OF_TRANSFER:		s = "ET_REQ";		break;	default:		s = "NO_REQ";		break;	}	printk(KERN_INFO "%s: IO: status -> 0x%02x (%s%s%s%s)\n",		cosa->name,		status,		status & SR_USR_RQ ? "USR_RQ|":"",		status & SR_TX_RDY ? "TX_RDY|":"",		status & SR_RX_RDY ? "RX_RDY|":"",		s);}static void debug_status_out(struct cosa_data *cosa, int status){	printk(KERN_INFO "%s: IO: status <- 0x%02x (%s%s%s%s%s%s)\n",		cosa->name,		status,		status & SR_RX_DMA_ENA  ? "RXDMA|":"!rxdma|",		status & SR_TX_DMA_ENA  ? "TXDMA|":"!txdma|",		status & SR_RST         ? "RESET|":"",		status & SR_USR_INT_ENA ? "USRINT|":"!usrint|",		status & SR_TX_INT_ENA  ? "TXINT|":"!txint|",		status & SR_RX_INT_ENA  ? "RXINT":"!rxint");}static void debug_data_in(struct cosa_data *cosa, int data){	printk(KERN_INFO "%s: IO: data -> 0x%04x\n", cosa->name, data);}static void debug_data_out(struct cosa_data *cosa, int data){	printk(KERN_INFO "%s: IO: data <- 0x%04x\n", cosa->name, data);}static void debug_data_cmd(struct cosa_data *cosa, int data){	printk(KERN_INFO "%s: IO: data <- 0x%04x (%s|%s)\n",		cosa->name, data,		data & SR_RDY_RCV ? "RX_RDY" : "!rx_rdy",		data & SR_RDY_SND ? "TX_RDY" : "!tx_rdy");}#endif/* EOF -- this file has not been truncated */

⌨️ 快捷键说明

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