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

📄 mac_scc.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
static void rs_flush_chars(struct tty_struct *tty){	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))		return;	if (info->xmit_cnt <= 0 || tty->stopped || info->private->tx_stopped ||	    !info->xmit_buf)		return;	/* Enable transmitter */	save_flags(flags); cli();	transmit_chars(info);	restore_flags(flags);}static int rs_write(struct tty_struct * tty, int from_user,		    const unsigned char *buf, int count){	int	c, total = 0;	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_write"))		return 0;	if (!tty || !info->xmit_buf)		return 0;	save_flags(flags);	while (1) {		cli();				c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				   SERIAL_XMIT_SIZE - info->xmit_head));		if (c <= 0)			break;		if (from_user) {			down(&tmp_buf_sem);			memcpy_fromfs(tmp_buf, buf, c);			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				       SERIAL_XMIT_SIZE - info->xmit_head));			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);			up(&tmp_buf_sem);		} else			memcpy(info->xmit_buf + info->xmit_head, buf, c);		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);		info->xmit_cnt += c;		restore_flags(flags);		buf += c;		count -= c;		total += c;	}	if (info->xmit_cnt && !tty->stopped && !info->tx_stopped	    && !info->tx_active)		transmit_chars(info);	restore_flags(flags);	return total;}#endif/* * ------------------------------------------------------------ * rs_throttle() *  * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void SCC_throttle(struct m68k_async_struct *info, int status){	unsigned long	flags;	save_flags(flags); 	cli();	if (status) {		/*		 * Here we want to turn off the RTS line.  On Macintoshes,		 * we only get the DTR line, which goes to both DTR and		 * RTS on the modem.  RTS doesn't go out to the serial		 * port socket.  So you should make sure your modem is		 * set to ignore DTR if you're using CRTSCTS.		 */		info->private->curregs[5] &= ~(DTR | RTS);		info->private->pendregs[5] &= ~(DTR | RTS);		write_zsreg(info->private->zs_channel, 5, 			    info->private->curregs[5]);	} else {		/* Assert RTS and DTR lines */		info->private->curregs[5] |= DTR | RTS;		info->private->pendregs[5] |= DTR | RTS;		write_zsreg(info->private->zs_channel, 5, 			    info->private->curregs[5]);	}	restore_flags(flags);}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static void SCC_get_serial_info(struct m68k_async_struct * info,			   struct serial_struct * retinfo){	retinfo->baud_base = info->baud_base;	retinfo->custom_divisor = info->custom_divisor;}/* FIXME: set_serial_info needs check_custom_divisor !!! *//* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * 	    is emptied.  On bus types like RS485, the transmitter must * 	    release the bus after transmitting. This must be done when * 	    the transmit shift register is empty, not be done when the * 	    transmit holding register is empty.  This functionality * 	    allows an RS485 driver to be written in user space.  */static int SCC_get_lsr_info(struct m68k_async_struct * info, unsigned int *value){	unsigned char status;	cli();	status = read_zsreg(info->private->zs_channel, 0);	sti();	return status;}static unsigned int SCC_get_modem_info(struct m68k_async_struct *info){	unsigned char control, status;	unsigned int result;	cli();	control = info->private->curregs[5];	status = read_zsreg(info->private->zs_channel, 0);	sti();	result =  ((control & RTS) ? TIOCM_RTS: 0)		| ((control & DTR) ? TIOCM_DTR: 0)		| ((status  & DCD) ? TIOCM_CAR: 0)		| ((status  & CTS) ? 0: TIOCM_CTS);	return result;}/* FIXME: zs_setdtr was used in rs_open ... */static int SCC_set_modem_info(struct m68k_async_struct *info, 			      int new_dtr, int new_rts){	unsigned int bits;	bits = (new_rts ? RTS: 0) + (new_dtr ? DTR: 0);	info->private->curregs[5] = (info->private->curregs[5] & ~(DTR | RTS)) | bits;	info->private->pendregs[5] = info->private->curregs[5];	write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);	sti();	return 0;}/* * This routine sends a break character out the serial port. */static void SCC_set_break(struct m68k_async_struct * info, int break_flag){	unsigned long	flags;	save_flags(flags);	cli();	if (break_flag) {		info->private->curregs[5] |= SND_BRK;		write_zsreg(info->private->zs_channel, 5, 			    info->private->curregs[5]);	} else {		info->private->curregs[5] &= ~SND_BRK;		write_zsreg(info->private->zs_channel, 5, 			    info->private->curregs[5]);	}	restore_flags(flags);}/* FIXME: these have to be enabled in rs_ioctl !! */static int SCC_ioctl(struct tty_struct *tty, struct file * file,		    struct m68k_async_struct * info, unsigned int cmd, 	            unsigned long arg){	int error;	switch (cmd) {		case TIOCSERGETLSR: /* Get line status register */			error = verify_area(VERIFY_WRITE, (void *) arg,				sizeof(unsigned int));			if (error)				return error;			else			    return SCC_get_lsr_info(info, (unsigned int *) arg);		case TIOCSERGSTRUCT:			error = verify_area(VERIFY_WRITE, (void *) arg,						sizeof(struct m68k_async_struct));			if (error)				return error;			copy_to_user((struct m68k_async_struct *) arg,				      info, sizeof(struct m68k_async_struct));			return 0;					default:			return -ENOIOCTLCMD;		}	return 0;}static void SCC_stop_receive (struct m68k_async_struct *info){	/* disable Rx */	info->private->curregs[3] &= ~RxENABLE;	info->private->pendregs[3] = info->private->curregs[3];	write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);	/* disable Rx interrupts */	info->private->curregs[1] &= ~(0x18);	/* disable any rx ints */	info->private->pendregs[1] = info->private->curregs[1];	write_zsreg(info->private->zs_channel, 1, info->private->curregs[1]);	ZS_CLEARFIFO(info->private->zs_channel);}static int SCC_trans_empty (struct m68k_async_struct *info){	return  (read_zsreg(info->private->zs_channel, 1) & ALL_SNT) != 0;}/* Finally, routines used to initialize the serial driver. */#ifdef CONFIG_MAC/* *	Mac: use boot_info data; assume 2 channels */ static void probe_sccs(void){	int n;#define ZS_CONTROL	0x50F04000#define ZS_DATA		(ZS_CONTROL+4)#define ZS_IRQ		5#define ZS_MOVE		-2#define ZS_DATA_MOVE	4#define ZS_CH_A_FIRST	2	/* last-ditch fixup for NetBSD booter case */	if (mac_bi_data.sccbase == 0)		mac_bi_data.sccbase = ZS_CONTROL;	/* testing: fix up broken 24 bit addresses (ClassicII) */	if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase)		mac_bi_data.sccbase |= 0x50000000;			if ( !hwreg_present((void *)mac_bi_data.sccbase))	{		printk(KERN_WARNING "z8530: Serial devices not accessible. Check serial switch.\n");		return;	}	for(n=0;n<2;n++)	{#if 0		zs_channels[n].control = (volatile unsigned char *)				ZS_CONTROL+ZS_MOVE*n;		zs_channels[n].data = (volatile unsigned char *)ZS_DATA+ZS_MOVE*n;#else		zs_channels[n].control = (volatile unsigned char *) /* 2, 0 */			(mac_bi_data.sccbase+ZS_CH_A_FIRST)+ZS_MOVE*n;		zs_channels[n].data = (volatile unsigned char *) /* 6, 4 */			(mac_bi_data.sccbase+ZS_CH_A_FIRST+ZS_DATA_MOVE)+ZS_MOVE*n;#endif		zs_soft[n].private = &zs_soft_private[n];		zs_soft[n].private->zs_channel = &zs_channels[n];		zs_soft[n].irq = IRQ4;#if 0				if (request_irq(ch->intrs[0], rs_interrupt, 0,				"SCC", &zs_soft[n]))			panic("macserial: can't get irq %d",			      ch->intrs[0]);#endif		if (n & 1)			zs_soft[n].private->zs_chan_a = &zs_channels[n-1];		else			zs_soft[n].private->zs_chan_a = &zs_channels[n];	}	zs_channels_found=2;}#else/* *	PowerMAC - query the PROM */ static void show_serial_version(void){	printk("PowerMac Z8530 serial driver version 1.00\n");}/* Ask the PROM how many Z8530s we have and initialize their zs_channels */static voidprobe_sccs(){	struct device_node *dev, *ch;	struct m68k_async_struct **pp;	int n;	n = 0;	pp = &zs_chain;	for (dev = find_devices("escc"); dev != 0; dev = dev->next) {		if (n >= NUM_CHANNELS) {			printk("Sorry, can't use %s: no more channels\n",			       dev->full_name);			continue;		}		for (ch = dev->child; ch != 0; ch = ch->sibling) {			if (ch->n_addrs < 1 || ch ->n_intrs < 1) {				printk("Can't use %s: %d addrs %d intrs\n",				      ch->full_name, ch->n_addrs, ch->n_intrs);				continue;			}			zs_channels[n].control = (volatile unsigned char *)				ch->addrs[0].address;			zs_channels[n].data = zs_channels[n].control				+ ch->addrs[0].size / 2;			zs_soft[n].private = &zs_soft_private[n];			zs_soft[n].private->zs_channel = &zs_channels[n];			zs_soft[n].irq = ch->intrs[0];			if (request_irq(ch->intrs[0], mac_SCC_interrupt, 0,					"SCC", &zs_soft[n]))				panic("macserial: can't get irq %d",				      ch->intrs[0]);			/* XXX this assumes the prom puts chan A before B */			if (n & 1)				zs_soft[n].private->zs_chan_a = &zs_channels[n-1];			else				zs_soft[n].private->zs_chan_a = &zs_channels[n];			*pp = &zs_soft[n];			pp = &zs_soft[n].private->zs_next;			++n;		}	}	*pp = 0;	zs_channels_found = n;}#endifextern void register_console(void (*proc)(const char *));static inline voidrs_cons_check(struct m68k_async_struct *ss, int channel){	int i, o, io;	static int consout_registered = 0;	static int msg_printed = 0;	i = o = io = 0;	/* Is this one of the serial console lines? */	if ((zs_cons_chanout != channel) &&	    (zs_cons_chanin != channel))		return;	zs_conschan = ss->private->zs_channel;	zs_consinfo = ss;	/* Register the console output putchar, if necessary */	if (zs_cons_chanout == channel) {		o = 1;		/* double whee.. */		if (!consout_registered) {			register_console(zs_console_print);			consout_registered = 1;		}	}	if (zs_cons_chanin == channel) {		i = 1;	}	if (o && i)		io = 1;	if (ss->private->zs_baud != 9600)		panic("Console baud rate weirdness");	/* Set flag variable for this port so that it cannot be	 * opened for other uses by accident.	 */	ss->private->is_cons = 1;	if (io) {		if(!msg_printed) {			printk("zs%d: console I/O\n", ((channel>>1)&1));			msg_printed = 1;		}	} else {		printk("zs%d: console %s\n", ((channel>>1)&1),		       (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));	}	/* FIXME : register interrupt here??? */}volatile int test_done;/* rs_init inits the driver */int mac_SCC_init(void){	int channel, line, nr = 0;	unsigned long flags;	struct serial_struct req;	printk("Mac68K Z8530 serial driver version 1.01\n");	/* SCC present at all? */	if (!MACH_IS_MAC)		return( -ENODEV );	if (zs_chain == 0)		probe_sccs();	save_flags(flags);	cli();	/* 	 * FIXME: init of rs_table entry and register_serial now done,	 * but possible clash of zs_soft[channel] and rs_table[channel]!!	 * zs_soft initialized in probe_sccs(), some settings copied to	 * info = &rs_table[channel], which is used by the mid-level code.	 * The info->private part is shared among both!	 */	for (channel = 0; channel < zs_channels_found; ++channel) {		req.line = channel;		req.type = SER_SCC_MAC;		req.port = (int) zs_soft[channel].private->zs_channel->control;		if ((line = register_serial( &req )) >= 0) {			SCC_init_port( &rs_table[line], req.type, line );			++nr;		}		else			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );	}	restore_flags(flags);	return( nr > 0 ? 0 : -ENODEV );}/* Hooks for running a serial console.  con_init() calls this if the * console is being run over one of the serial ports. * 'channel' is decoded as 0=modem 1=printer, 'chip' is ignored. */voidrs_cons_hook(int chip, int out, int channel){	if (zs_chain == 0)		probe_sccs();	zs_soft[channel].private->clk_divisor = 16;	zs_soft[channel].private->zs_baud = get_zsbaud(&zs_soft[channel]);	rs_cons_check(&zs_soft[channel], channel);	if (out)		zs_cons_chanout = channel;	else		zs_cons_chanin = channel;	/* FIXME : register interrupt here??? */}/* This is called at boot time to prime the kgdb serial debugging * serial line.  The 'tty_num' argument is 0 for /dev/ttyS0 and 1 * for /dev/ttyS1 which is determined in setup_arch() from the * boot command line flags. */voidrs_kgdb_hook(int tty_num){	if (zs_chain == 0)		probe_sccs();	zs_kgdbchan = zs_soft[tty_num].private->zs_channel;	zs_soft[tty_num].private->clk_divisor = 16;	zs_soft[tty_num].private->zs_baud = get_zsbaud(&zs_soft[tty_num]);	zs_soft[tty_num].private->kgdb_channel = 1;     /* This runs kgdb */	zs_soft[tty_num ^ 1].private->kgdb_channel = 0; /* This does not */	/* Turn on transmitter/receiver at 8-bits/char */	kgdb_chaninit(&zs_soft[tty_num], 0, 9600);	ZS_CLEARERR(zs_kgdbchan);	ZS_CLEARFIFO(zs_kgdbchan);	/* FIXME : register interrupt here??? */}

⌨️ 快捷键说明

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