serial167.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,382 行 · 第 1/5 页

C
2,382
字号
	interruptible_sleep_on(&info->close_wait);	if (info->flags & ASYNC_HUP_NOTIFY){	    return -EAGAIN;	}else{	    return -ERESTARTSYS;	}    }    /*     * If non-blocking mode is set, then make the check up front     * and then exit.     */    if (filp->f_flags & O_NONBLOCK) {	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: %s, count = %d\n",	   tty->name, 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) {	local_irq_save(flags);	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	local_irq_restore(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;	}	local_irq_save(flags);	    base_addr[CyCAR] = (u_char)channel;/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */	    if (!(info->flags & ASYNC_CLOSING)	    && (C_CLOCAL(tty)	        || (base_addr[CyMSVR1] & CyDCD))) {		    local_irq_restore(flags);		    break;	    }	local_irq_restore(flags);	if (signal_pending(current)) {	    retval = -ERESTARTSYS;	    break;	}#ifdef SERIAL_DEBUG_OPEN	printk("block_til_ready blocking: %s, count = %d\n",	       tty->name, 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: %s, count = %d\n",	   tty->name, 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 = tty->index;    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 %s\n", tty->name); /* */#endif    if (serial_paranoia_check(info, tty->name, "cy_open")){        return -ENODEV;    }#ifdef SERIAL_DEBUG_OPEN    printk("cy_open %s, count = %d\n", tty->name, 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_zeroed_page(GFP_KERNEL);	if (!tmp_buf){	    return -ENOMEM;        }    }    /*     * 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;    }#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;	local_irq_save(flags);	/*	 * 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){            local_irq_restore(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){            local_irq_restore(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);	local_irq_restore(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 */static struct tty_operations cy_ops = {	.open = cy_open,	.close = cy_close,	.write = cy_write,	.put_char = cy_put_char,	.flush_chars = cy_flush_chars,	.write_room = cy_write_room,	.chars_in_buffer = cy_chars_in_buffer,	.flush_buffer = cy_flush_buffer,	.ioctl = cy_ioctl,	.throttle = cy_throttle,	.unthrottle = cy_unthrottle,	.set_termios = cy_set_termios,	.stop = cy_stop,	.start = cy_start,	.hangup = cy_hangup,	.tiocmget = cy_tiocmget,	.tiocmset = cy_tiocmset,};/* 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. */static int __initserial167_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;    cy_serial_driver = alloc_tty_driver(NR_PORTS);    if (!cy_serial_driver)	return -ENOMEM;#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	serial_console = 64; /*callout_driver.minor_start*/#endif    }    /* Initialize the tty_driver structure */        cy_serial_driver->owner = THIS_MODULE;    cy_serial_driver->devfs_name = "tts/";    cy_serial_driver->name = "ttyS";    cy_serial_driver->major = TTY_MAJOR;    cy_serial_driver->minor_start = 64;    cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL;    cy_serial_driver->subtype = SERIAL_TYPE_NORMAL;    cy_serial_driver->init_termios = tty_std_termios;    cy_serial_driver->init_termios.c_cflag =	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;    cy_serial_driver->flags = TTY_DRIVER_REAL_RAW;    tty_set_operations(cy_serial_driver, &cy_ops);    ret = tty_register_driver(cy_serial_driver);    if (ret) {	    printk(KERN_ERR "Couldn't register MVME166/7 serial driver\n");	    put_tty_driver(cy_serial_driver);	    return ret;    }    init_bh(CYCLADES_BH, do_cyclades_bh);    port_num = 0;    info = cy_port;    for (index = 0; index < 1; index++) {	good_ports = 4;	if(port_num < NR_PORTS){	    while( good_ports-- && port_num < NR_PORTS){		/*** initialize port ***/		info->magic = CYCLADES_MAGIC;		info->type = PORT_CIRRUS;		info->card = index;		info->line = port_num;		info->flags = STD_COM_FLAGS;		info->tty = 0;		info->xmit_fifo_size = 12;		info->cor1 = CyPARITY_NONE|Cy_8_BITS;		info->cor2 = CyETC;		info->cor3 = Cy_1_STOP;		info->cor4 = 0x08; /* _very_ small receive threshold */		info->cor5 = 0;		info->cor6 = 0;		info->cor7 = 0;		info->tbpr = baud_bpr[DefSpeed]; /* Tx BPR */		info->tco = baud_co[DefSpeed]; /* Tx CO */		info->rbpr = baud_bpr[DefSpeed]; /* Rx BPR */		info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */		info->close_delay = 0;		info->x_char = 0;		info->event = 0;		info->count = 0;#ifdef SERIAL_DEBUG_COUNT    printk("cyc: %d: setting count to 0\n", __LINE__);#endif		info->blocked_open = 0;		info->default_threshold = 0;		info->default_timeout = 0;		info->tqueue.routine = do_softint;		info->tqueue.data = info;		init_waitqueue_head(&info->open_wait);		init_waitqueue_head(&info->close_wait);		/* info->session */		/* info->pgrp *//*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/		info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK                                       | CyPARITY| CyFRAME| CyOVERRUN;		/* info->timeout */		printk("ttyS%d ", info->line);		port_num++;info++;		if(!(port_num & 7)){		    printk("\n               ");		}	    }	}	printk("\n");    }    while( port_num < NR_PORTS){	info->line = -1;	port_num++;info++;    }#ifdef CONFIG_REMOTE_DEBUG    debug_setup();#endif    ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0,				"cd2401_errors", cd2401_rxerr_interrupt);    if (ret) {	    printk(KERN_ERR "Could't get cd2401_errors IRQ");	    goto clean

⌨️ 快捷键说明

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