⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sdio_uart.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	 */	(void) sdio_in(port, UART_LSR);	(void) sdio_in(port, UART_RX);	(void) sdio_in(port, UART_IIR);	(void) sdio_in(port, UART_MSR);	/*	 * Now, initialize the UART	 */	sdio_out(port, UART_LCR, UART_LCR_WLEN8);	port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;	port->mctrl = TIOCM_OUT2;	sdio_uart_change_speed(port, port->tty->termios, NULL);	if (port->tty->termios->c_cflag & CBAUD)		sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);	if (port->tty->termios->c_cflag & CRTSCTS)		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))			port->tty->hw_stopped = 1;	clear_bit(TTY_IO_ERROR, &port->tty->flags);	/* Kick the IRQ handler once while we're still holding the host lock */	sdio_uart_irq(port->func);	sdio_uart_release_func(port);	return 0;err3:	sdio_disable_func(port->func);err2:	sdio_uart_release_func(port);err1:	free_page((unsigned long)port->xmit.buf);	return ret;}static void sdio_uart_shutdown(struct sdio_uart_port *port){	int ret;	ret = sdio_uart_claim_func(port);	if (ret)		goto skip;	sdio_uart_stop_rx(port);	/* TODO: wait here for TX FIFO to drain */	/* Turn off DTR and RTS early. */	if (port->tty->termios->c_cflag & HUPCL)		sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);	 /* Disable interrupts from this port */	sdio_release_irq(port->func);	port->ier = 0;	sdio_out(port, UART_IER, 0);	sdio_uart_clear_mctrl(port, TIOCM_OUT2);	/* Disable break condition and FIFOs. */	port->lcr &= ~UART_LCR_SBC;	sdio_out(port, UART_LCR, port->lcr);	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |				 UART_FCR_CLEAR_RCVR |				 UART_FCR_CLEAR_XMIT);	sdio_out(port, UART_FCR, 0);	sdio_disable_func(port->func);	sdio_uart_release_func(port);skip:	/* Free the transmit buffer page. */	free_page((unsigned long)port->xmit.buf);}static int sdio_uart_open (struct tty_struct *tty, struct file * filp){	struct sdio_uart_port *port;	int ret;	port = sdio_uart_port_get(tty->index);	if (!port)		return -ENODEV;	mutex_lock(&port->open_lock);	/*	 * Make sure not to mess up with a dead port	 * which has not been closed yet.	 */	if (tty->driver_data && tty->driver_data != port) {		mutex_unlock(&port->open_lock);		sdio_uart_port_put(port);		return -EBUSY;	}	if (!port->opened) {		tty->driver_data = port;		port->tty = tty;		ret = sdio_uart_startup(port);		if (ret) {			tty->driver_data = NULL;			port->tty = NULL;			mutex_unlock(&port->open_lock);			sdio_uart_port_put(port);			return ret;		}	}	port->opened++;	mutex_unlock(&port->open_lock);	return 0;}static void sdio_uart_close(struct tty_struct *tty, struct file * filp){	struct sdio_uart_port *port = tty->driver_data;	if (!port)		return;	mutex_lock(&port->open_lock);	BUG_ON(!port->opened);	/*	 * This is messy.  The tty layer calls us even when open()	 * returned an error.  Ignore this close request if tty->count	 * is larger than port->count.	 */	if (tty->count > port->opened) {		mutex_unlock(&port->open_lock);		return;	}	if (--port->opened == 0) {		tty->closing = 1;		sdio_uart_shutdown(port);		tty_ldisc_flush(tty);		port->tty = NULL;		tty->driver_data = NULL;		tty->closing = 0;	}	mutex_unlock(&port->open_lock);	sdio_uart_port_put(port);}static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,			   int count){	struct sdio_uart_port *port = tty->driver_data;	struct circ_buf *circ = &port->xmit;	int c, ret = 0;	if (!port->func)		return -ENODEV;	spin_lock(&port->write_lock);	while (1) {		c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);		if (count < c)			c = count;		if (c <= 0)			break;		memcpy(circ->buf + circ->head, buf, c);		circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);		buf += c;		count -= c;		ret += c;	}	spin_unlock(&port->write_lock);	if ( !(port->ier & UART_IER_THRI)) {		int err = sdio_uart_claim_func(port);		if (!err) {			sdio_uart_start_tx(port);			sdio_uart_irq(port->func);			sdio_uart_release_func(port);		} else			ret = err;	}	return ret;}static int sdio_uart_write_room(struct tty_struct *tty){	struct sdio_uart_port *port = tty->driver_data;	return port ? circ_chars_free(&port->xmit) : 0;}static int sdio_uart_chars_in_buffer(struct tty_struct *tty){	struct sdio_uart_port *port = tty->driver_data;	return port ? circ_chars_pending(&port->xmit) : 0;}static void sdio_uart_send_xchar(struct tty_struct *tty, char ch){	struct sdio_uart_port *port = tty->driver_data;	port->x_char = ch;	if (ch && !(port->ier & UART_IER_THRI)) {		if (sdio_uart_claim_func(port) != 0)			return;		sdio_uart_start_tx(port);		sdio_uart_irq(port->func);		sdio_uart_release_func(port);	}}static void sdio_uart_throttle(struct tty_struct *tty){	struct sdio_uart_port *port = tty->driver_data;	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))		return;	if (sdio_uart_claim_func(port) != 0)		return;	if (I_IXOFF(tty)) {		port->x_char = STOP_CHAR(tty);		sdio_uart_start_tx(port);	}	if (tty->termios->c_cflag & CRTSCTS)		sdio_uart_clear_mctrl(port, TIOCM_RTS);	sdio_uart_irq(port->func);	sdio_uart_release_func(port);}static void sdio_uart_unthrottle(struct tty_struct *tty){	struct sdio_uart_port *port = tty->driver_data;	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))		return;	if (sdio_uart_claim_func(port) != 0)		return;	if (I_IXOFF(tty)) {		if (port->x_char) {			port->x_char = 0;		} else {			port->x_char = START_CHAR(tty);			sdio_uart_start_tx(port);		}	}	if (tty->termios->c_cflag & CRTSCTS)		sdio_uart_set_mctrl(port, TIOCM_RTS);	sdio_uart_irq(port->func);	sdio_uart_release_func(port);}static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios){	struct sdio_uart_port *port = tty->driver_data;	unsigned int cflag = tty->termios->c_cflag;#define RELEVANT_IFLAG(iflag)   ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))	if ((cflag ^ old_termios->c_cflag) == 0 &&	    RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)		return;	if (sdio_uart_claim_func(port) != 0)		return;	sdio_uart_change_speed(port, tty->termios, old_termios);	/* Handle transition to B0 status */	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))		sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR);	/* Handle transition away from B0 status */	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {		unsigned int mask = TIOCM_DTR;		if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))			mask |= TIOCM_RTS;		sdio_uart_set_mctrl(port, mask);	}	/* Handle turning off CRTSCTS */	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {		tty->hw_stopped = 0;		sdio_uart_start_tx(port);	}	/* Handle turning on CRTSCTS */	if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) {			tty->hw_stopped = 1;			sdio_uart_stop_tx(port);		}	}	sdio_uart_release_func(port);}static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state){	struct sdio_uart_port *port = tty->driver_data;	if (sdio_uart_claim_func(port) != 0)		return;	if (break_state == -1)		port->lcr |= UART_LCR_SBC;	else		port->lcr &= ~UART_LCR_SBC;	sdio_out(port, UART_LCR, port->lcr);	sdio_uart_release_func(port);}static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file){	struct sdio_uart_port *port = tty->driver_data;	int result;	result = sdio_uart_claim_func(port);	if (!result) {		result = port->mctrl | sdio_uart_get_mctrl(port);		sdio_uart_release_func(port);	}	return result;}static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,			      unsigned int set, unsigned int clear){	struct sdio_uart_port *port = tty->driver_data;	int result;	result =sdio_uart_claim_func(port);	if(!result) {		sdio_uart_update_mctrl(port, set, clear);		sdio_uart_release_func(port);	}	return result;}static int sdio_uart_read_proc(char *page, char **start, off_t off,			       int count, int *eof, void *data){	int i, len = 0;	off_t begin = 0;	len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",		       "", "", "");	for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) {		struct sdio_uart_port *port = sdio_uart_port_get(i);		if (port) {			len += sprintf(page+len, "%d: uart:SDIO", i);			if(capable(CAP_SYS_ADMIN)) {				len += sprintf(page + len, " tx:%d rx:%d",					       port->icount.tx, port->icount.rx);				if (port->icount.frame)					len += sprintf(page + len, " fe:%d",						       port->icount.frame);				if (port->icount.parity)					len += sprintf(page + len, " pe:%d",						       port->icount.parity);				if (port->icount.brk)					len += sprintf(page + len, " brk:%d",						       port->icount.brk);				if (port->icount.overrun)					len += sprintf(page + len, " oe:%d",						       port->icount.overrun);				if (port->icount.cts)					len += sprintf(page + len, " cts:%d",						       port->icount.cts);				if (port->icount.dsr)					len += sprintf(page + len, " dsr:%d",						       port->icount.dsr);				if (port->icount.rng)					len += sprintf(page + len, " rng:%d",						       port->icount.rng);				if (port->icount.dcd)					len += sprintf(page + len, " dcd:%d",						       port->icount.dcd);			}			strcat(page, "\n");			len++;			sdio_uart_port_put(port);		}		if (len + begin > off + count)			goto done;		if (len + begin < off) {			begin += len;			len = 0;		}	}	*eof = 1;done:	if (off >= len + begin)		return 0;	*start = page + (off - begin);	return (count < begin + len - off) ? count : (begin + len - off);}static const struct tty_operations sdio_uart_ops = {	.open			= sdio_uart_open,	.close			= sdio_uart_close,	.write			= sdio_uart_write,	.write_room		= sdio_uart_write_room,	.chars_in_buffer	= sdio_uart_chars_in_buffer,	.send_xchar		= sdio_uart_send_xchar,	.throttle		= sdio_uart_throttle,	.unthrottle		= sdio_uart_unthrottle,	.set_termios		= sdio_uart_set_termios,	.break_ctl		= sdio_uart_break_ctl,	.tiocmget		= sdio_uart_tiocmget,	.tiocmset		= sdio_uart_tiocmset,	.read_proc		= sdio_uart_read_proc,};static struct tty_driver *sdio_uart_tty_driver;static int sdio_uart_probe(struct sdio_func *func,			   const struct sdio_device_id *id){	struct sdio_uart_port *port;	int ret;	port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);	if (!port)		return -ENOMEM;	if (func->class == SDIO_CLASS_UART) {		printk(KERN_WARNING "%s: need info on UART class basic setup\n",		       sdio_func_id(func));		kfree(port);		return -ENOSYS;	} else if (func->class == SDIO_CLASS_GPS) {		/*		 * We need tuple 0x91.  It contains SUBTPL_SIOREG		 * and SUBTPL_RCVCAPS.		 */		struct sdio_func_tuple *tpl;		for (tpl = func->tuples; tpl; tpl = tpl->next) {			if (tpl->code != 0x91)				continue;			if (tpl->size < 10)				continue;			if (tpl->data[1] == 0)  /* SUBTPL_SIOREG */				break;		}		if (!tpl) {			printk(KERN_WARNING			       "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",			       sdio_func_id(func));			kfree(port);			return -EINVAL;		}		printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",		       sdio_func_id(func), tpl->data[2], tpl->data[3]);		port->regs_offset = (tpl->data[4] << 0) |				    (tpl->data[5] << 8) |				    (tpl->data[6] << 16);		printk(KERN_DEBUG "%s: regs offset = 0x%x\n",		       sdio_func_id(func), port->regs_offset);		port->uartclk = tpl->data[7] * 115200;		if (port->uartclk == 0)			port->uartclk = 115200;		printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",		       sdio_func_id(func), port->uartclk,		       tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));	} else {		kfree(port);		return -EINVAL;	}	port->func = func;	sdio_set_drvdata(func, port);	ret = sdio_uart_add_port(port);	if (ret) {		kfree(port);	} else {		struct device *dev;		dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev);		if (IS_ERR(dev)) {			sdio_uart_port_remove(port);			ret = PTR_ERR(dev);		}	}	return ret;}static void sdio_uart_remove(struct sdio_func *func){	struct sdio_uart_port *port = sdio_get_drvdata(func);	tty_unregister_device(sdio_uart_tty_driver, port->index);	sdio_uart_port_remove(port);}static const struct sdio_device_id sdio_uart_ids[] = {	{ SDIO_DEVICE_CLASS(SDIO_CLASS_UART)		},	{ SDIO_DEVICE_CLASS(SDIO_CLASS_GPS)		},	{ /* end: all zeroes */				},};MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);static struct sdio_driver sdio_uart_driver = {	.probe		= sdio_uart_probe,	.remove		= sdio_uart_remove,	.name		= "sdio_uart",	.id_table	= sdio_uart_ids,};static int __init sdio_uart_init(void){	int ret;	struct tty_driver *tty_drv;	sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR);	if (!tty_drv)		return -ENOMEM;	tty_drv->owner = THIS_MODULE;	tty_drv->driver_name = "sdio_uart";	tty_drv->name =   "ttySDIO";	tty_drv->major = 0;  /* dynamically allocated */	tty_drv->minor_start = 0;	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;	tty_drv->subtype = SERIAL_TYPE_NORMAL;	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;	tty_drv->init_termios = tty_std_termios;	tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;	tty_drv->init_termios.c_ispeed = 4800;	tty_drv->init_termios.c_ospeed = 4800;	tty_set_operations(tty_drv, &sdio_uart_ops);	ret = tty_register_driver(tty_drv);	if (ret)		goto err1;	ret = sdio_register_driver(&sdio_uart_driver);	if (ret)		goto err2;	return 0;err2:	tty_unregister_driver(tty_drv);err1:	put_tty_driver(tty_drv);	return ret;}static void __exit sdio_uart_exit(void){	sdio_unregister_driver(&sdio_uart_driver);	tty_unregister_driver(sdio_uart_tty_driver);	put_tty_driver(sdio_uart_tty_driver);}module_init(sdio_uart_init);module_exit(sdio_uart_exit);MODULE_AUTHOR("Nicolas Pitre");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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