📄 sunserial.c
字号:
else retval = -ERESTARTSYS; #else retval = -EAGAIN;#endif break; } if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && !(info->flags & ZILOG_CLOSING) && do_clocal) break; if (current->signal & ~current->blocked) { retval = -ERESTARTSYS; break; }#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttys%d, count = %d\n", info->line, info->count);#endif schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); if (!tty_hung_up_p(filp)) info->count++; info->blocked_open--;#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttys%d, count = %d\n", info->line, info->count);#endif if (retval) return retval; info->flags |= ZILOG_NORMAL_ACTIVE; return 0;} /* * This routine is called whenever a serial port is opened. It * enables interrupts for a serial port, linking in its ZILOG structure into * the IRQ chain. It also performs the serial-specific * initialization for the tty structure. */int rs_open(struct tty_struct *tty, struct file * filp){ struct sun_serial *info; int retval, line; line = MINOR(tty->device) - tty->driver.minor_start; /* The zilog lines for the mouse/keyboard must be * opened using their respective drivers. */ if ((line < 0) || (line >= NUM_CHANNELS)) return -ENODEV; if((line == KEYBOARD_LINE) || (line == MOUSE_LINE)) return -ENODEV; info = zs_soft + line; /* Is the kgdb running over this line? */ if (info->kgdb_channel) return -ENODEV; if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV;#ifdef SERIAL_DEBUG_OPEN printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->count);#endif info->count++; tty->driver_data = info; info->tty = tty; /* * Start up serial port */ retval = startup(info); if (retval) return retval; retval = block_til_ready(tty, filp, info); if (retval) {#ifdef SERIAL_DEBUG_OPEN printk("rs_open returning after block_til_ready with %d\n", retval);#endif return retval; } if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) *tty->termios = info->normal_termios; else *tty->termios = info->callout_termios; change_speed(info); } info->session = current->session; info->pgrp = current->pgrp;#ifdef SERIAL_DEBUG_OPEN printk("rs_open ttys%d successful...", info->line);#endif return 0;}/* Finally, routines used to initialize the serial driver. */static void show_serial_version(void){ printk("Sparc Zilog8530 serial driver version 1.00\n");}/* Probe the PROM for the request zs chip number. */static inline struct sun_zslayout *get_zs(int chip){ struct linux_prom_irqs tmp_irq; unsigned long paddr = 0; unsigned long vaddr = 0; int zsnode, tmpnode, iospace, slave; static int irq = 0;#if CONFIG_AP1000 printk("No zs chip\n"); return NULL;#endif iospace = 0; if(chip < 0 || chip >= NUM_SERIAL) panic("get_zs bogon zs chip number"); if(sparc_cpu_model == sun4) { /* Grrr, these have to be hardcoded aieee */ switch(chip) { case 0: paddr = 0xf1000000; break; case 1: paddr = 0xf0000000; break; }; iospace = 0; zs_nodes[chip] = 0; if(!irq) zilog_irq = irq = 12; vaddr = (unsigned long) sparc_alloc_io((char *) paddr, 0, 8, "Zilog Serial", iospace, 0); } else { /* Can use the prom for other machine types */ zsnode = prom_getchild(prom_root_node); tmpnode = prom_searchsiblings(zsnode, "obio"); if(tmpnode) zsnode = prom_getchild(tmpnode); if(!zsnode) panic("get_zs no zs serial prom node"); while(zsnode) { zsnode = prom_searchsiblings(zsnode, "zs"); slave = prom_getintdefault(zsnode, "slave", -1); if(slave==chip) { /* The one we want */ vaddr = (unsigned long) prom_getintdefault(zsnode, "address", 0xdeadbeef); if(vaddr == 0xdeadbeef) prom_halt(); zs_nodes[chip] = zsnode; prom_getproperty(zsnode, "intr", (char *) &tmp_irq, sizeof(tmp_irq));#ifdef OLD_STYLE_IRQ tmp_irq.pri &= 0xf;#endif if(!irq) { irq = zilog_irq = tmp_irq.pri; } else { if(tmp_irq.pri != irq) panic("zilog: bogon irqs"); } break; } zsnode = prom_getsibling(zsnode); } if(!zsnode) panic("get_zs whee chip not found"); } if(!vaddr) panic("get_zs whee no serial chip mappable"); return (struct sun_zslayout *) vaddr;}extern void register_console(void (*proc)(const char *));static inline voidrs_cons_check(struct sun_serial *ss, int channel){ int i, o, io; static consout_registered = 0; static 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->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 this is console input, we handle the break received * status interrupt on this line to mean prom_halt(). */ if(zs_cons_chanin == channel) { ss->break_abort = 1; i = 1; } if(o && i) io = 1; if(ss->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->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"))); }}volatile int test_done;extern void keyboard_zsinit(void);extern void sun_mouse_zsinit(void);/* rs_init inits the driver */int rs_init(void){ int chip, channel, i, flags; struct sun_serial *info;#if CONFIG_AP1000 printk("not doing rs_init()\n"); return 0;#endif /* Setup base handler, and timer table. */ init_bh(SERIAL_BH, do_serial_bh); timer_table[RS_TIMER].fn = rs_timer; timer_table[RS_TIMER].expires = 0; show_serial_version(); /* Initialize the tty_driver structure */ /* SPARC: Not all of this is exactly right for us. */ memset(&serial_driver, 0, sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; serial_driver.name = "ttyS"; serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64; serial_driver.num = NUM_CHANNELS; serial_driver.type = TTY_DRIVER_TYPE_SERIAL; serial_driver.subtype = SERIAL_TYPE_NORMAL; serial_driver.init_termios = tty_std_termios; serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; serial_driver.flags = TTY_DRIVER_REAL_RAW; serial_driver.refcount = &serial_refcount; serial_driver.table = serial_table; serial_driver.termios = serial_termios; serial_driver.termios_locked = serial_termios_locked; serial_driver.open = rs_open; serial_driver.close = rs_close; serial_driver.write = rs_write; serial_driver.flush_chars = rs_flush_chars; serial_driver.write_room = rs_write_room; serial_driver.chars_in_buffer = rs_chars_in_buffer; serial_driver.flush_buffer = rs_flush_buffer; serial_driver.ioctl = rs_ioctl; serial_driver.throttle = rs_throttle; serial_driver.unthrottle = rs_unthrottle; serial_driver.set_termios = rs_set_termios; serial_driver.stop = rs_stop; serial_driver.start = rs_start; serial_driver.hangup = rs_hangup; /* * The callout device is just like normal device except for * major number and the subtype code. */ callout_driver = serial_driver; callout_driver.name = "cua"; callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; if (tty_register_driver(&serial_driver)) panic("Couldn't register serial driver\n"); if (tty_register_driver(&callout_driver)) panic("Couldn't register callout driver\n"); save_flags(flags); cli(); /* Set up our interrupt linked list */ zs_chain = &zs_soft[0]; zs_soft[0].zs_next = &zs_soft[1]; zs_soft[1].zs_next = &zs_soft[2]; zs_soft[2].zs_next = &zs_soft[3]; zs_soft[3].zs_next = 0; for(chip = 0; chip < NUM_SERIAL; chip++) { /* If we are doing kgdb over one of the channels on * chip zero, kgdb_channel will be set to 1 by the * rs_kgdb_hook() routine below. */ if(!zs_chips[chip]) { zs_chips[chip] = get_zs(chip); /* Two channels per chip */ zs_channels[(chip*2)] = &zs_chips[chip]->channelA; zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB; zs_soft[(chip*2)].kgdb_channel = 0; zs_soft[(chip*2)+1].kgdb_channel = 0; } /* First, set up channel A on this chip. */ channel = chip * 2; zs_soft[channel].zs_channel = zs_channels[channel]; zs_soft[channel].change_needed = 0; zs_soft[channel].clk_divisor = 16; zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); zs_soft[channel].cons_mouse = 0; /* If not keyboard/mouse and is console serial * line, then enable receiver interrupts. */ if((channel<KEYBOARD_LINE) && (zs_soft[channel].is_cons)) { write_zsreg(zs_soft[channel].zs_channel, R1, (EXT_INT_ENAB | INT_ALL_Rx)); write_zsreg(zs_soft[channel].zs_channel, R9, (NV | MIE)); write_zsreg(zs_soft[channel].zs_channel, R10, (NRZ)); write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE)); write_zsreg(zs_soft[channel].zs_channel, R5, (Tx8 | TxENAB)); } /* If this is the kgdb line, enable interrupts because we * now want to receive the 'control-c' character from the * client attached to us asynchronously. */ if(zs_soft[channel].kgdb_channel) kgdb_chaninit(&zs_soft[channel], 1, zs_soft[channel].zs_baud); if(channel == KEYBOARD_LINE) { /* Tell keyboard driver about our presence. */ if(zs_soft[channel].zs_baud != 1200) panic("Weird keyboard serial baud rate"); zs_soft[channel].cons_keyb = 1; zs_kbdchan = zs_soft[channel].zs_channel; /* Enable Rx/Tx, IRQs, and inform kbd driver */ write_zsreg(zs_soft[channel].zs_channel, R1, (EXT_INT_ENAB | INT_ALL_Rx)); write_zsreg(zs_soft[channel].zs_channel, R4, (PAR_EVEN | X16CLK | SB1)); write_zsreg(zs_soft[channel].zs_channel, R9, (NV|MIE)); write_zsreg(zs_soft[channel].zs_channel, R10, (NRZ)); write_zsreg(zs_soft[channel].zs_channel, R11, (TCBR | RCBR)); write_zsreg(zs_soft[channel].zs_channel, R14, (BRSRC | BRENABL)); write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE)); write_zsreg(zs_soft[channel].zs_channel, R5, (Tx8 | TxENAB | DTR | RTS));#if 0 write_zsreg(zs_soft[channel].zs_channel, R15, (DCDIE | CTSIE | TxUIE | BRKIE));#endif ZS_CLEARERR(zs_soft[channel].zs_channel); ZS_CLEARFIFO(zs_soft[channel].zs_channel); } /* Now, channel B */ channel++; zs_soft[channel].zs_channel = zs_channels[channel]; zs_soft[channel].change_needed = 0; zs_soft[channel].clk_divisor = 16; zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); zs_soft[channel].cons_keyb = 0; /* If not keyboard/mouse and is console serial * line, then enable receiver interrupts. */ if(channel<KEYBOARD_LINE && zs_soft[channel].is_cons) { write_zsreg(zs_soft[channel].zs_channel, R1, (EXT_INT_ENAB | INT_ALL_Rx)); write_zsreg(zs_soft[channel].zs_channel, R9, (NV | MIE)); write_zsreg(zs_soft[channel].zs_channel, R10, (NRZ)); write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE)); write_zsreg(zs_soft[channel].zs_channel, R5, (Tx8 | TxENAB | RTS | DTR)); } if(channel == MOUSE_LINE) { /* Tell mouse driver about our presence. */ if(zs_soft[channel].zs_baud != 1200) panic("Weird mouse serial baud rate"); zs_soft[channel].cons_mouse = 1; zs_mousechan = zs_soft[channel].zs_channel; /* Enable Rx, IRQs, and inform mouse driver */ write_zsreg(zs_soft[channel].zs_channel, R1, (INT_ALL_Rx)); write_zsreg(zs_soft[channel].zs_channel, R9, (NV|MIE)); write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE));#if 0 /* XXX hangs sun4c's sometimes */ write_zsreg(zs_soft[channel].zs_channel, R15, (DCDIE | CTSIE | TxUIE | BRKIE));#endif sun_mouse_zsinit(); } else { zs_soft[channel].cons_mouse = 0; } } for(info=zs_chain, i=0; info; info = info->zs_next, i++) { info->magic = SERIAL_MAGIC; info->port = (int) info->zs_channel; info->line = i; info->tty = 0; info->irq = zilog_irq; info->custom_divisor = 16; info->close_delay = 50; info->closing_wait = 3000; info->x_char = 0; info->event = 0; info->count = 0; info->blocked_open = 0; info->tqueue.routine = do_softint; info->tqueue.data = info; info->tqueue_hangup.routine = do_serial_hangup; info->tqueue_hangup.data = info; info->callout_termios =callout_driver.init_termios; info->normal_termios = serial_driver.init_termios; info->open_wait = 0; info->close_wait = 0; printk("tty%02d at 0x%04x (irq = %d)", info->line, info->port, info->irq); printk(" is a Zilog8530\n"); } if (request_irq(zilog_irq, rs_interrupt, (SA_INTERRUPT | SA_STATIC_ALLOC), "Zilog8530", NULL)) panic("Unable to attach zs intr\n"); restore_flags(flags); keyboard_zsinit(); return 0;}/* * register_serial and unregister_serial allows for serial ports to be * configured at run-time, to support PCMCIA modems. *//* SPARC: Unused at this time, just here to make things link. */int register_serial(struct serial_struct *req){ return -1;}void unregister_serial(int line){ return;}/* Hooks for running a serial console. con_init() calls this if the * console is being run over one of the ttya/ttyb serial ports. * 'chip' should be zero, as chip 1 drives the mouse/keyboard. * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels * are addressed backwards, channel B is first, then channel A. */voidrs_cons_hook(int chip, int out, int channel){ if(chip) panic("rs_cons_hook called with chip not zero"); if(!zs_chips[chip]) { zs_chips[chip] = get_zs(chip); /* Two channels per chip */ zs_channels[(chip*2)] = &zs_chips[chip]->channelA; zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB; } zs_soft[channel].zs_channel = zs_channels[channel]; zs_soft[channel].change_needed = 0; zs_soft[channel].clk_divisor = 16; zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); rs_cons_check(&zs_soft[channel], channel); if(out) zs_cons_chanout = ((chip * 2) + channel); else zs_cons_chanin = ((chip * 2) + channel);}/* This is called at boot time to prime the kgdb serial debugging * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1 * for /dev/ttyb which is determined in setup_arch() from the * boot command line flags. */voidrs_kgdb_hook(int tty_num){ int chip = 0; if(!zs_chips[chip]) { zs_chips[chip] = get_zs(chip); /* Two channels per chip */ zs_channels[(chip*2)] = &zs_chips[chip]->channelA; zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB; } zs_soft[tty_num].zs_channel = zs_channels[tty_num]; zs_kgdbchan = zs_soft[tty_num].zs_channel; zs_soft[tty_num].change_needed = 0; zs_soft[tty_num].clk_divisor = 16; zs_soft[tty_num].zs_baud = get_zsbaud(&zs_soft[tty_num]); zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */ zs_soft[tty_num ^ 1].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); udelay(5); ZS_CLEARFIFO(zs_kgdbchan);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -