📄 macserial.c
字号:
printk(KERN_ERR "rs_close: bad serial port count for " "ttyS%d: %d\n", info->line, info->count); info->count = 0; } if (info->count) { MOD_DEC_USE_COUNT; restore_flags(flags); return; } info->flags |= ZILOG_CLOSING; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if (info->flags & ZILOG_NORMAL_ACTIVE) info->normal_termios = *tty->termios; if (info->flags & ZILOG_CALLOUT_ACTIVE) info->callout_termios = *tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ OPNDBG("waiting end of Tx... (timeout:%d)\n", info->closing_wait); tty->closing = 1; if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) { restore_flags(flags); tty_wait_until_sent(tty, info->closing_wait); save_flags(flags); cli(); } /* * At this point we stop accepting input. To do this, we * disable the receiver and receive interrupts. */ info->curregs[3] &= ~RxENABLE; info->pendregs[3] = info->curregs[3]; write_zsreg(info->zs_channel, 3, info->curregs[3]); info->curregs[1] &= ~(0x18); /* disable any rx ints */ info->pendregs[1] = info->curregs[1]; write_zsreg(info->zs_channel, 1, info->curregs[1]); ZS_CLEARFIFO(info->zs_channel); if (info->flags & ZILOG_INITIALIZED) { /* * Before we drop DTR, make sure the SCC transmitter * has completely drained. */ OPNDBG("waiting end of Rx...\n"); restore_flags(flags); rs_wait_until_sent(tty, info->timeout); save_flags(flags); cli(); } shutdown(info); /* restore flags now since shutdown() will have disabled this port's specific irqs */ restore_flags(flags); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); MOD_DEC_USE_COUNT;}/* * rs_wait_until_sent() --- wait until the transmitter is empty */static void rs_wait_until_sent(struct tty_struct *tty, int timeout){ struct mac_serial *info = (struct mac_serial *) tty->driver_data; unsigned long orig_jiffies, char_time; if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) return;/* printk("rs_wait_until_sent, timeout:%d, tty_stopped:%d, tx_stopped:%d\n", timeout, tty->stopped, info->tx_stopped);*/ orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check * interval should also be less than the timeout. */ if (info->timeout <= HZ/50) { printk(KERN_INFO "macserial: invalid info->timeout=%d\n", info->timeout); info->timeout = HZ/50+1; } char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; char_time = char_time / 5; if (char_time > HZ) { printk(KERN_WARNING "macserial: char_time %ld >HZ !!!\n", char_time); char_time = 1; } else if (char_time == 0) char_time = 1; if (timeout) char_time = MIN(char_time, timeout); while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } current->state = TASK_RUNNING;}/* * rs_hangup() --- called by tty_hangup() when a hangup is signaled. */static void rs_hangup(struct tty_struct *tty){ struct mac_serial * info = (struct mac_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_hangup")) return; rs_flush_buffer(tty); shutdown(info); info->event = 0; info->count = 0; info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE); info->tty = 0; wake_up_interruptible(&info->open_wait);}/* * ------------------------------------------------------------ * rs_open() and friends * ------------------------------------------------------------ */static int block_til_ready(struct tty_struct *tty, struct file * filp, struct mac_serial *info){ DECLARE_WAITQUEUE(wait,current); int retval; int do_clocal = 0; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (info->flags & ZILOG_CLOSING) { interruptible_sleep_on(&info->close_wait);#ifdef SERIAL_DO_RESTART return ((info->flags & ZILOG_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);#else return -EAGAIN;#endif } /* * If this is a callout device, then just make sure the normal * device isn't being used. */ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { if (info->flags & ZILOG_NORMAL_ACTIVE) return -EBUSY; if ((info->flags & ZILOG_CALLOUT_ACTIVE) && (info->flags & ZILOG_SESSION_LOCKOUT) && (info->session != current->session)) return -EBUSY; if ((info->flags & ZILOG_CALLOUT_ACTIVE) && (info->flags & ZILOG_PGRP_LOCKOUT) && (info->pgrp != current->pgrp)) return -EBUSY; info->flags |= ZILOG_CALLOUT_ACTIVE; return 0; } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { if (info->flags & ZILOG_CALLOUT_ACTIVE) return -EBUSY; info->flags |= ZILOG_NORMAL_ACTIVE; return 0; } if (info->flags & ZILOG_CALLOUT_ACTIVE) { if (info->normal_termios.c_cflag & CLOCAL) do_clocal = 1; } else { if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, info->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait); OPNDBG("block_til_ready before block: ttyS%d, count = %d\n", info->line, info->count); cli(); if (!tty_hung_up_p(filp)) info->count--; sti(); info->blocked_open++; while (1) { cli(); if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD) && !info->is_irda) zs_rtsdtr(info, 1); sti(); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ZILOG_INITIALIZED)) {#ifdef SERIAL_DO_RESTART if (info->flags & ZILOG_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS;#else retval = -EAGAIN;#endif break; } if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && !(info->flags & ZILOG_CLOSING) && (do_clocal || (read_zsreg(info->zs_channel, 0) & DCD))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } OPNDBG("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); if (!tty_hung_up_p(filp)) info->count++; info->blocked_open--; OPNDBG("block_til_ready after blocking: ttyS%d, count = %d\n", info->line, info->count); 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. */static int rs_open(struct tty_struct *tty, struct file * filp){ struct mac_serial *info; int retval, line; unsigned long page; MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= zs_channels_found)) { MOD_DEC_USE_COUNT; return -ENODEV; } info = zs_soft + line;#ifdef CONFIG_KGDB if (info->kgdb_channel) { MOD_DEC_USE_COUNT; return -ENODEV; }#endif if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV; OPNDBG("rs_open %s%d, count = %d, tty=%p\n", tty->driver.name, info->line, info->count, tty); info->count++; tty->driver_data = info; info->tty = tty; if (!tmp_buf) { page = get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; if (tmp_buf) free_page(page); else tmp_buf = (unsigned char *) page; } /* * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->flags & ZILOG_CLOSING)) { if (info->flags & ZILOG_CLOSING) interruptible_sleep_on(&info->close_wait);#ifdef SERIAL_DO_RESTART return ((info->flags & ZILOG_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);#else return -EAGAIN;#endif } /* * Start up serial port */ retval = startup(info); if (retval) return retval; retval = block_til_ready(tty, filp, info); if (retval) { OPNDBG("rs_open returning after block_til_ready with %d\n", retval); 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, 0); }#ifdef CONFIG_SERIAL_CONSOLE if (sercons.cflag && sercons.index == line) { tty->termios->c_cflag = sercons.cflag; sercons.cflag = 0; change_speed(info, 0); }#endif info->session = current->session; info->pgrp = current->pgrp; OPNDBG("rs_open ttyS%d successful...\n", info->line); return 0;}/* Finally, routines used to initialize the serial driver. */static void show_serial_version(void){ printk(KERN_INFO "PowerMac Z8530 serial driver version " MACSERIAL_VERSION "\n");}/* * Initialize one channel, both the mac_serial and mac_zschannel * structs. We use the dev_node field of the mac_serial struct. */static intchan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan, struct mac_zschannel *zs_chan_a){ struct device_node *ch = zss->dev_node; char *conn; int len; struct slot_names_prop { int count; char name[1]; } *slots; zss->irq = ch->intrs[0].line; zss->has_dma = 0;#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA) if (ch->n_addrs >= 3 && ch->n_intrs == 3) zss->has_dma = 1;#endif zss->dma_initted = 0; zs_chan->control = (volatile unsigned char *) ioremap(ch->addrs[0].address, 0x1000); zs_chan->data = zs_chan->control + 0x10; spin_lock_init(&zs_chan->lock); zs_chan->parent = zss; zss->zs_channel = zs_chan; zss->zs_chan_a = zs_chan_a; /* setup misc varariables */ zss->kgdb_channel = 0; /* For now, we assume you either have a slot-names property * with "Modem" in it, or your channel is compatible with * "cobalt". Might need additional fixups */ zss->is_internal_modem = device_is_compatible(ch, "cobalt"); conn = get_property(ch, "AAPL,connector", &len); zss->is_irda = conn && (strcmp(conn, "infrared") == 0); zss->port_type = PMAC_SCC_ASYNC; /* 1999 Powerbook G3 has slot-names property instead */ slots = (struct slot_names_prop *)get_property(ch, "slot-names", &len); if (slots && slots->count > 0) { if (strcmp(slots->name, "IrDA") == 0) zss->is_irda = 1; else if (strcmp(slots->name, "Modem") == 0) zss->is_internal_modem = 1; } if (zss->is_irda) zss->port_type = PMAC_SCC_IRDA; if (zss->is_internal_modem) { struct device_node* i2c_modem = find_devices("i2c-modem"); if (i2c_modem) { char* mid = get_property(i2c_modem, "modem-id", NULL); if (mid) switch(*mid) { case 0x04 : case 0x05 : case 0x07 : case 0x08 : case 0x0b : case 0x0c : zss->port_type = PMAC_SCC_I2S1; } printk(KERN_INFO "macserial: i2c-modem detected, id: %d\n", mid ? (*mid) : 0); } else { printk(KERN_INFO "macserial: serial modem detected\n"); } } while (zss->has_dma) { zss->dma_priv = NULL; /* it seems that the last two addresses are the DMA controllers */ zss->tx_dma = (volatile struct dbdma_regs *) ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100); zss->rx = (volatile struct mac_dma *) ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100); zss->tx_dma_irq = ch->intrs[1].line; zss->rx_dma_irq = ch->intrs[2].line; spin_lock_init(&zss->rx_dma_lock); break; } init_timer(&zss->powerup_timer); zss->powerup_timer.function = powerup_done; zss->powerup_timer.data = (unsigned long) zss; return 0;}/* * /proc fs routines. TODO: Add status lines & error stats */static inline intline_info(char *buf, struct mac_serial *info){ int ret=0; unsigned char* connector; int lenp; ret += sprintf(buf, "%d: port:0x%X irq:%d", info->line, info->port, info->irq); connector = get_property(info->dev_node, "AAPL,connector", &lenp); if (connector) ret+=sprintf(buf+ret," con:%s ", connector); if (info->is_internal_modem) { if (!connector) ret+=sprintf(buf+ret," con:"); ret+=sprintf(buf+ret,"%s", " (internal modem)"); } if (info->is_irda) { if (!connector) ret+=sprintf(buf+ret," con:"); ret+=sprintf(buf+ret,"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -