📄 serial167.c
字号:
tty->ldisc.flush_buffer(tty); info->event = 0; info->tty = 0; if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (tty->ldisc.open) (tty->ldisc.open)(tty); } 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 &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| ASYNC_CLOSING); wake_up_interruptible(&info->close_wait);#ifdef SERIAL_DEBUG_OTHER printk("cy_close done\n");#endif return;} /* cy_close *//* * cy_hangup() --- called by tty_hangup() when a hangup is signaled. */voidcy_hangup(struct tty_struct *tty){ struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; #ifdef SERIAL_DEBUG_OTHER printk("cy_hangup ttyS%d\n", info->line); /* */#endif if (serial_paranoia_check(info, tty->device, "cy_hangup")) return; shutdown(info);#if 0 info->event = 0; info->count = 0;#ifdef SERIAL_DEBUG_COUNT printk("cyc: %d: setting count to 0\n", __LINE__);#endif info->tty = 0;#endif info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); wake_up_interruptible(&info->open_wait);} /* cy_hangup *//* * ------------------------------------------------------------ * cy_open() and friends * ------------------------------------------------------------ */static intblock_til_ready(struct tty_struct *tty, struct file * filp, struct cyclades_port *info){ DECLARE_WAITQUEUE(wait, current); unsigned long flags; int channel; int retval; volatile u_char *base_addr = (u_char *)BASE_ADDR; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (info->flags & ASYNC_CLOSING) { interruptible_sleep_on(&info->close_wait); if (info->flags & ASYNC_HUP_NOTIFY){ return -EAGAIN; }else{ return -ERESTARTSYS; } } /* * 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 & ASYNC_NORMAL_ACTIVE){ return -EBUSY; } if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_SESSION_LOCKOUT) && (info->session != current->session)){ return -EBUSY; } if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_PGRP_LOCKOUT) && (info->pgrp != current->pgrp)){ return -EBUSY; } info->flags |= ASYNC_CALLOUT_ACTIVE; return 0; } /* * If non-blocking mode is set, then make the check up front * and then exit. */ if (filp->f_flags & O_NONBLOCK) { if (info->flags & ASYNC_CALLOUT_ACTIVE){ return -EBUSY; } info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } /* * 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 * cy_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait);#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttyS%d, count = %d\n", info->line, info->count);/**/#endif info->count--;#ifdef SERIAL_DEBUG_COUNT printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count);#endif info->blocked_open++; channel = info->line; while (1) { save_flags(flags); cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ base_addr[CyCAR] = (u_char)channel; base_addr[CyMSVR1] = CyRTS;/* CP('S');CP('4'); */ base_addr[CyMSVR2] = CyDTR;#ifdef SERIAL_DEBUG_DTR printk("cyc: %d: raising DTR\n", __LINE__); printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);#endif } restore_flags(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED) ){ if (info->flags & ASYNC_HUP_NOTIFY) { retval = -EAGAIN; }else{ retval = -ERESTARTSYS; } break; } save_flags(flags); cli(); base_addr[CyCAR] = (u_char)channel;/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || (base_addr[CyMSVR1] & CyDCD))) { restore_flags(flags); break; } restore_flags(flags); if (signal_pending(current)) { 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++;#ifdef SERIAL_DEBUG_COUNT printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);#endif } 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 |= ASYNC_NORMAL_ACTIVE; return 0;} /* block_til_ready *//* * This routine is called whenever a serial port is opened. It * performs the serial-specific initialization for the tty structure. */intcy_open(struct tty_struct *tty, struct file * filp){ struct cyclades_port *info; int retval, line;/* CP('O'); */ line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (NR_PORTS <= line)){ return -ENODEV; } info = &cy_port[line]; if (info->line < 0){ return -ENODEV; }#ifdef SERIAL_DEBUG_OTHER printk("cy_open ttyS%d\n", info->line); /* */#endif if (serial_paranoia_check(info, tty->device, "cy_open")){ return -ENODEV; }#ifdef SERIAL_DEBUG_OPEN printk("cy_open ttyS%d, count = %d\n", info->line, info->count);/**/#endif info->count++;#ifdef SERIAL_DEBUG_COUNT printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);#endif tty->driver_data = info; info->tty = tty; if (!tmp_buf) { tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); if (!tmp_buf){ return -ENOMEM; } } if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) *tty->termios = info->normal_termios; else *tty->termios = info->callout_termios; } /* * 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("cy_open returning after block_til_ready with %d\n", retval);#endif return retval; } info->session = current->session; info->pgrp = current->pgrp;#ifdef SERIAL_DEBUG_OPEN printk("cy_open done\n");/**/#endif return 0;} /* cy_open *//* * --------------------------------------------------------------------- * serial167_init() and friends * * serial167_init() is called at boot-time to initialize the serial driver. * --------------------------------------------------------------------- *//* * This routine prints out the appropriate serial driver version * number, and identifies which options were configured into this * driver. */static voidshow_version(void){ printk("MVME166/167 cd2401 driver\n");} /* show_version *//* initialize chips on card -- return number of valid chips (which is number of ports/4) *//* * This initialises the hardware to a reasonable state. It should * probe the chip first so as to copy 166-Bug setup as a default for * port 0. It initialises CMR to CyASYNC; that is never done again, so * as to limit the number of CyINIT_CHAN commands in normal running. * * ... I wonder what I should do if this fails ... */voidmvme167_serial_console_setup(int cflag){ volatile unsigned char* base_addr = (u_char *)BASE_ADDR; int ch; u_char spd; u_char rcor, rbpr, badspeed = 0; unsigned long flags; save_flags(flags); cli(); /* * First probe channel zero of the chip, to see what speed has * been selected. */ base_addr[CyCAR] = 0; rcor = base_addr[CyRCOR] << 5; rbpr = base_addr[CyRBPR]; for (spd = 0; spd < sizeof(baud_bpr); spd++) if (rbpr == baud_bpr[spd] && rcor == baud_co[spd]) break; if (spd >= sizeof(baud_bpr)) { spd = 14; /* 19200 */ badspeed = 1; /* Failed to identify speed */ } initial_console_speed = spd; /* OK, we have chosen a speed, now reset and reinitialise */ my_udelay(20000L); /* Allow time for any active o/p to complete */ if(base_addr[CyCCR] != 0x00){ restore_flags(flags); /* printk(" chip is never idle (CCR != 0)\n"); */ return; } base_addr[CyCCR] = CyCHIP_RESET; /* Reset the chip */ my_udelay(1000L); if(base_addr[CyGFRCR] == 0x00){ restore_flags(flags); /* printk(" chip is not responding (GFRCR stayed 0)\n"); */ return; } /* * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms * tick */ base_addr[CyTPR] = 10; base_addr[CyPILR1] = 0x01; /* Interrupt level for modem change */ base_addr[CyPILR2] = 0x02; /* Interrupt level for tx ints */ base_addr[CyPILR3] = 0x03; /* Interrupt level for rx ints */ /* * Attempt to set up all channels to something reasonable, and * bang out a INIT_CHAN command. We should then be able to limit * the ammount of fiddling we have to do in normal running. */ for (ch = 3; ch >= 0 ; ch--) { base_addr[CyCAR] = (u_char)ch; base_addr[CyIER] = 0; base_addr[CyCMR] = CyASYNC; base_addr[CyLICR] = (u_char)ch << 2; base_addr[CyLIVR] = 0x5c; base_addr[CyTCOR] = baud_co[spd]; base_addr[CyTBPR] = baud_bpr[spd]; base_addr[CyRCOR] = baud_co[spd] >> 5; base_addr[CyRBPR] = baud_bpr[spd]; base_addr[CySCHR1] = 'Q' & 0x1f; base_addr[CySCHR2] = 'X' & 0x1f; base_addr[CySCRL] = 0; base_addr[CySCRH] = 0; base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; base_addr[CyCOR2] = 0; base_addr[CyCOR3] = Cy_1_STOP; base_addr[CyCOR4] = baud_cor4[spd]; base_addr[CyCOR5] = 0; base_addr[CyCOR6] = 0; base_addr[CyCOR7] = 0; base_addr[CyRTPRL] = 2; base_addr[CyRTPRH] = 0; base_addr[CyMSVR1] = 0; base_addr[CyMSVR2] = 0; write_cy_cmd(base_addr,CyINIT_CHAN|CyDIS_RCVR|CyDIS_XMTR); } /* * Now do specials for channel zero.... */ base_addr[CyMSVR1] = CyRTS; base_addr[CyMSVR2] = CyDTR; base_addr[CyIER] = CyRxData; write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR); restore_flags(flags); my_udelay(20000L); /* Let it all settle down */ printk("CD2401 initialised, chip is rev 0x%02x\n", base_addr[CyGFRCR]); if (badspeed) printk(" WARNING: Failed to identify line speed, rcor=%02x,rbpr=%02x\n", rcor >> 5, rbpr);} /* serial_console_init *//* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a first found, first allocated manner. That is, this code searches for Cyclom cards in the system. As each is found, it is probed to discover how many chips (and thus how many ports) are present. These ports are mapped to the tty ports 64 and upward in monotonic fashion. If an 8-port card is replaced with a 16-port card, the port mapping on a following card will shift. This approach is different from what is used in the other serial device driver because the Cyclom is more properly a multiplexer, not just an aggregation of serial ports on one card. If there are more cards with more ports than have been statically allocated above, a warning is printed and the extra ports are ignored. */intserial167_init(void){ struct cyclades_port *info; int ret = 0; int good_ports = 0; int port_num = 0; int index; int DefSpeed;#ifdef notyet struct sigaction sa;#endif if (!(mvme16x_config &MVME16x_CONFIG_GOT_CD2401)) return 0;#if 0scrn[1] = '\0';#endif show_version(); /* Has "console=0,9600n8" been used in bootinfo to change speed? */ if (serial_console_cflag) DefSpeed = serial_console_cflag & 0017; else { DefSpeed = initial_console_speed; serial_console_info = &cy_port[0]; serial_console_cflag = DefSpeed | CS8;#if 0 ser
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -