📄 mac_scc.c
字号:
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 + -