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

📄 macserial.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	if (info->tty && C_CRTSCTS(info->tty)) {		/*		 * For some reason, on the Power Macintosh,		 * it seems that the CTS bit is 1 when CTS is		 * *negated* and 0 when it is asserted.		 * The DCD bit doesn't seem to be inverted		 * like this.		 */		if ((status & CTS) == 0) {			if (info->tx_stopped) {#ifdef SERIAL_DEBUG_FLOW				printk("CTS up\n");#endif				info->tx_stopped = 0;				if (!info->tx_active)					transmit_chars(info);			}		} else {#ifdef SERIAL_DEBUG_FLOW			printk("CTS down\n");#endif			info->tx_stopped = 1;		}	}	/* Clear status condition... */	write_zsreg(info->zs_channel, 0, RES_EXT_INT);	info->read_reg_zero = status;}static _INLINE_ void receive_special_dma(struct mac_serial *info){	unsigned char stat, flag;	volatile struct dbdma_regs *rd = &info->rx->dma;	int where = RX_BUF_SIZE;	spin_lock(&info->rx_dma_lock);	if ((ld_le32(&rd->status) & ACTIVE) != 0)		dbdma_flush(rd);	if (in_le32(&rd->cmdptr)	    == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1))		where -= in_le16(&info->rx->res_count);	where--;	stat = read_zsreg(info->zs_channel, R1);	flag = stat_to_flag(stat);	if (flag) {		info->rx_flag_buf[info->rx_cbuf][where] = flag;		/* reset the error indication */		write_zsreg(info->zs_channel, 0, ERR_RES);	}	spin_unlock(&info->rx_dma_lock);}/* * This is the serial driver's generic interrupt routine */static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){	struct mac_serial *info = (struct mac_serial *) dev_id;	unsigned char zs_intreg;	int shift;	if (!(info->flags & ZILOG_INITIALIZED)) {		printk("rs_interrupt: irq %d, port not initialized\n", irq);		disable_irq(irq);		return;	}	/* NOTE: The read register 3, which holds the irq status,	 *       does so for both channels on each chip.  Although	 *       the status value itself must be read from the A	 *       channel and is only valid when read from channel A.	 *       Yes... broken hardware...	 */#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)	if (info->zs_chan_a == info->zs_channel)		shift = 3;	/* Channel A */	else		shift = 0;	/* Channel B */	for (;;) {		zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;#ifdef SERIAL_DEBUG_INTR		printk("rs_interrupt: irq %d, zs_intreg 0x%x\n",		       irq, (int)zs_intreg);#endif		if ((zs_intreg & CHAN_IRQMASK) == 0)			break;		if (zs_intreg & CHBRxIP) {			/* If we are doing DMA, we only ask for interrupts			   on characters with errors or special conditions. */			if (info->dma_initted)				receive_special_dma(info);			else				receive_chars(info, regs);		}		if (zs_intreg & CHBTxIP)			transmit_chars(info);		if (zs_intreg & CHBEXT)			status_handle(info);	}}/* Transmit DMA interrupt - not used at present */static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs){}/* * Receive DMA interrupt. */static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs){	struct mac_serial *info = (struct mac_serial *) dev_id;	volatile struct dbdma_cmd *cd;	if (!info->dma_initted)		return;	spin_lock(&info->rx_dma_lock);	/* First, confirm that this interrupt is, indeed, coming */	/* from Rx DMA */	cd = info->rx_cmds[info->rx_cbuf] + 2;	if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) {		spin_unlock(&info->rx_dma_lock);		return;	}	if (info->rx_fbuf != RX_NO_FBUF) {		info->rx_cbuf = info->rx_fbuf;		if (++info->rx_fbuf == info->rx_nbuf)			info->rx_fbuf = 0;		if (info->rx_fbuf == info->rx_ubuf)			info->rx_fbuf = RX_NO_FBUF;	}	spin_unlock(&info->rx_dma_lock);}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * ------------------------------------------------------------ */static void rs_stop(struct tty_struct *tty){	struct mac_serial *info = (struct mac_serial *)tty->driver_data;#ifdef SERIAL_DEBUG_STOP	printk("rs_stop %ld....\n",	       tty->ldisc.chars_in_buffer(tty));#endif	if (serial_paranoia_check(info, tty->device, "rs_stop"))		return;#if 0	save_flags(flags); cli();	if (info->curregs[5] & TxENAB) {		info->curregs[5] &= ~TxENAB;		info->pendregs[5] &= ~TxENAB;		write_zsreg(info->zs_channel, 5, info->curregs[5]);	}	restore_flags(flags);#endif}static void rs_start(struct tty_struct *tty){	struct mac_serial *info = (struct mac_serial *)tty->driver_data;	unsigned long flags;#ifdef SERIAL_DEBUG_STOP	printk("rs_start %ld....\n", 	       tty->ldisc.chars_in_buffer(tty));#endif	if (serial_paranoia_check(info, tty->device, "rs_start"))		return;	save_flags(flags); cli();#if 0	if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {		info->curregs[5] |= TxENAB;		info->pendregs[5] = info->curregs[5];		write_zsreg(info->zs_channel, 5, info->curregs[5]);	}#else	if (info->xmit_cnt && info->xmit_buf && !info->tx_active) {		transmit_chars(info);	}#endif	restore_flags(flags);}/* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh(void){	run_task_queue(&tq_serial);}static void do_softint(void *private_){	struct mac_serial	*info = (struct mac_serial *) private_;	struct tty_struct	*tty;	tty = info->tty;	if (!tty)		return;	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&		    tty->ldisc.write_wakeup)			(tty->ldisc.write_wakeup)(tty);		wake_up_interruptible(&tty->write_wait);	}}static int startup(struct mac_serial * info, int can_sleep){	int delay;	OPNDBG("startup() (ttyS%d, irq %d)\n", info->line, info->irq); 	if (info->flags & ZILOG_INITIALIZED) {		OPNDBG(" -> already inited\n"); 		return 0;	}	if (!info->xmit_buf) {		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);		if (!info->xmit_buf)			return -ENOMEM;	}	OPNDBG("starting up ttyS%d (irq %d)...\n", info->line, info->irq);	delay = set_scc_power(info, 1);	setup_scc(info);	OPNDBG("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq);	info->flags |= ZILOG_INITIALIZED;	enable_irq(info->irq);	if (info->dma_initted) {		enable_irq(info->rx_dma_irq);	}	if (delay) {		if (can_sleep) {			/* we need to wait a bit before using the port */			current->state = TASK_INTERRUPTIBLE;			schedule_timeout(delay * HZ / 1000);		} else			mdelay(delay);	}	return 0;}static _INLINE_ void rxdma_start(struct mac_serial * info, int current){	volatile struct dbdma_regs *rd = &info->rx->dma;	volatile struct dbdma_cmd *cd = info->rx_cmds[current];//printk(KERN_DEBUG "SCC: rxdma_start\n");	st_le32(&rd->cmdptr, virt_to_bus(cd));	out_le32(&rd->control, (RUN << 16) | RUN);}static void rxdma_to_tty(struct mac_serial *info){	struct tty_struct	*tty = info->tty;	volatile struct dbdma_regs *rd = &info->rx->dma;	unsigned long flags;	int residue, available, space, do_queue;	if (!tty)		return;	do_queue = 0;	spin_lock_irqsave(&info->rx_dma_lock, flags);more:	space = TTY_FLIPBUF_SIZE - tty->flip.count;	if (!space) {		do_queue++;		goto out;	}	residue = 0;	if (info->rx_ubuf == info->rx_cbuf) {		if ((ld_le32(&rd->status) & ACTIVE) != 0) {			dbdma_flush(rd);			if (in_le32(&rd->cmdptr)			    == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1))				residue = in_le16(&info->rx->res_count);		}	}	available = RX_BUF_SIZE - residue - info->rx_done_bytes;	if (available > space)		available = space;	if (available) {		memcpy(tty->flip.char_buf_ptr,		       info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes,		       available);		memcpy(tty->flip.flag_buf_ptr,		       info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,		       available);		tty->flip.char_buf_ptr += available;		tty->flip.count += available;		tty->flip.flag_buf_ptr += available;		memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,		       0, available);		info->rx_done_bytes += available;		do_queue++;	}	if (info->rx_done_bytes == RX_BUF_SIZE) {		volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf];		if (info->rx_ubuf == info->rx_cbuf)			goto out;		/* mark rx_char_buf[rx_ubuf] free */		st_le16(&cd->command, DBDMA_NOP);		cd++;		st_le32(&cd->cmd_dep, 0);		st_le32((unsigned int *)&cd->res_count, 0);		cd++;		st_le16(&cd->xfer_status, 0);		if (info->rx_fbuf == RX_NO_FBUF) {			info->rx_fbuf = info->rx_ubuf;			if (!(ld_le32(&rd->status) & ACTIVE)) {				dbdma_reset(&info->rx->dma);				rxdma_start(info, info->rx_ubuf);				info->rx_cbuf = info->rx_ubuf;			}		}		info->rx_done_bytes = 0;		if (++info->rx_ubuf == info->rx_nbuf)			info->rx_ubuf = 0;		if (info->rx_fbuf == info->rx_ubuf)			info->rx_fbuf = RX_NO_FBUF;		goto more;	}out:	spin_unlock_irqrestore(&info->rx_dma_lock, flags);	if (do_queue)		queue_task(&tty->flip.tqueue, &tq_timer);}static void poll_rxdma(void *private_){	struct mac_serial	*info = (struct mac_serial *) private_;	unsigned long flags;	rxdma_to_tty(info);	spin_lock_irqsave(&info->rx_dma_lock, flags);	mod_timer(&info->poll_dma_timer, RX_DMA_TIMER);	spin_unlock_irqrestore(&info->rx_dma_lock, flags);}static void dma_init(struct mac_serial * info){	int i, size;	volatile struct dbdma_cmd *cd;	unsigned char *p;	info->rx_nbuf = 8;	/* various mem set up */	size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2)		+ (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds)		   + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf))		* info->rx_nbuf;	info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA);	if (info->dma_priv == NULL)		return;	memset(info->dma_priv, 0, size);	info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv;	info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf);	info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf;	p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf);	for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)		info->rx_char_buf[i] = p;	for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)		info->rx_flag_buf[i] = p;	/* a bit of DMA programming */	cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p);	st_le16(&cd->command, DBDMA_NOP);	cd++;	st_le16(&cd->req_count, RX_BUF_SIZE);	st_le16(&cd->command, INPUT_MORE);	st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0]));	cd++;	st_le16(&cd->req_count, 4);	st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);	st_le32(&cd->phy_addr, virt_to_bus(cd-2));	st_le32(&cd->cmd_dep, DBDMA_STOP);	for (i = 1; i < info->rx_nbuf; i++) {		info->rx_cmds[i] = ++cd;		st_le16(&cd->command, DBDMA_NOP);		cd++;		st_le16(&cd->req_count, RX_BUF_SIZE);		st_le16(&cd->command, INPUT_MORE);		st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i]));		cd++;		st_le16(&cd->req_count, 4);		st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);		st_le32(&cd->phy_addr, virt_to_bus(cd-2));		st_le32(&cd->cmd_dep, DBDMA_STOP);	}	cd++;	st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS);	st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0]));	/* setup DMA to our liking */	dbdma_reset(&info->rx->dma);	st_le32(&info->rx->dma.intr_sel, 0x10001);	st_le32(&info->rx->dma.br_sel, 0x10001);	out_le32(&info->rx->dma.wait_sel, 0x10001);	/* set various flags */	info->rx_ubuf = 0;	info->rx_cbuf = 0;	info->rx_fbuf = info->rx_ubuf + 1;	if (info->rx_fbuf == info->rx_nbuf)		info->rx_fbuf = RX_NO_FBUF;	info->rx_done_bytes = 0;	/* setup polling */	init_timer(&info->poll_dma_timer);	info->poll_dma_timer.function = (void *)&poll_rxdma;	info->poll_dma_timer.data = (unsigned long)info;	info->dma_initted = 1;}static int setup_scc(struct mac_serial * info){	unsigned long flags;	OPNDBG("setting up ttys%d SCC...\n", info->line);	save_flags(flags); cli(); /* Disable interrupts */	/*	 * Reset the chip.	 */	write_zsreg(info->zs_channel, 9,		    (info->zs_channel == info->zs_chan_a? CHRA: CHRB));	udelay(10);	write_zsreg(info->zs_channel, 9, 0);	/*	 * Clear the receive FIFO.	 */	ZS_CLEARFIFO(info->zs_channel);	info->xmit_fifo_size = 1;	/*	 * Reset DMAs	 */	if (info->has_dma)		dma_init(info);	/*	 * Clear the interrupt registers.	 */	write_zsreg(info->zs_channel, 0, ERR_RES);	write_zsreg(info->zs_channel, 0, RES_H_IUS);	/*	 * Turn on RTS and DTR.

⌨️ 快捷键说明

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