完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
#include #include #include #include #include #include #include #include #define FIFO_SIZE PAGE_SIZE __u32 cts; __u32 rng; __u32 rx; __u32 frame; __u32 parity; }; struct sdio_uart_port { struct kref kref; unsigned int index; struct mutex func_lock; unsigned int regs_offset; spinlock_t write_lock; unsigned int uartclk; unsigned int rx_mctrl; unsigned int ignore_status_mask; unsigned char ier; }; static struct sdio_uart_port *sdio_uart_table[UART_NR]; { mutex_init(&port->func_lock); if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL)) for (index = 0; index < UART_NR; index++) { port->index = index; ret = 0; } spin_unlock(&sdio_uart_table_lock); return ret; { return NULL; spin_lock(&sdio_uart_table_lock); if (port) spin_unlock(&sdio_uart_table_lock); return port; { container_of(kref, struct sdio_uart_port, kref); kfree(port); { } static void sdio_uart_port_remove(struct sdio_uart_port *port) struct sdio_func *func; sdio_uart_table[port->index] = NULL; * We're killing a port that potentially still is in use by * to the SDIO function and arrange for the tty layer to * Beware: the lock ordering is critical. mutex_lock(&port->port.mutex); func = port->func; port->func = NULL; tty = tty_port_tty_get(&port->port); if (tty) { tty_kref_put(tty); mutex_unlock(&port->port.mutex); sdio_disable_func(func); } static int sdio_uart_claim_func(struct sdio_uart_port *port) mutex_lock(&port->func_lock); mutex_unlock(&port->func_lock); } sdio_claim_host(port->func); return 0; { sdio_release_host(port->func); { c = sdio_readb(port->func, port->regs_offset + offset, NULL); } static inline void sdio_out(struct sdio_uart_port *port, int offset, int value) sdio_writeb(port->func, value, port->regs_offset + offset, NULL); { unsigned int ret; /* FIXME: What stops this losing the delta bits and breaking status = sdio_in(port, UART_MSR); ret = 0; ret |= TIOCM_CAR; ret |= TIOCM_RNG; ret |= TIOCM_DSR; ret |= TIOCM_CTS; } static void sdio_uart_write_mctrl(struct sdio_uart_port *port, { mcr |= UART_MCR_RTS; mcr |= UART_MCR_DTR; mcr |= UART_MCR_OUT1; mcr |= UART_MCR_OUT2; mcr |= UART_MCR_LOOP; sdio_out(port, UART_MCR, mcr); unsigned int set, unsigned int clear) unsigned int old; old = port->mctrl; if (old != port->mctrl) } #define sdio_uart_set_mctrl(port, x) sdio_uart_update_mctrl(port, x, 0) struct ktermios *termios, { unsigned int baud, quot; switch (termios->c_cflag & CSIZE) { cval = UART_LCR_WLEN5; case CS6: break; cval = UART_LCR_WLEN7; default: cval = UART_LCR_WLEN8; } if (termios->c_cflag & CSTOPB) if (termios->c_cflag & PARENB) if (!(termios->c_cflag & PARODD)) baud = tty_termios_baud_rate(termios); baud = 9600; /* Special case: B0 rate. */ break; * Oops, the quotient was zero. Try again with the old */ if (old) { old = NULL; termios->c_cflag |= B9600; quot = (2 * port->uartclk + baud) / (2 * baud); if (baud < 2400) else if (termios->c_iflag & INPCK) if (termios->c_iflag & (BRKINT | PARMRK)) * Characters to ignore port->ignore_status_mask = 0; port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; port->ignore_status_mask |= UART_LSR_BI; * If we're ignoring parity and break indicators, */ port->ignore_status_mask |= UART_LSR_OE; * ignore all characters if CREAD is not set if ((termios->c_cflag & CREAD) == 0) * CTS flow control flag and modem status interrupts port->ier &= ~UART_IER_MSI; port->ier |= UART_IER_MSI; port->lcr = cval; sdio_out(port, UART_IER, port->ier); sdio_out(port, UART_DLL, quot & 0xff); sdio_out(port, UART_LCR, cval); } static void sdio_uart_start_tx(struct sdio_uart_port *port) if (!(port->ier & UART_IER_THRI)) { sdio_out(port, UART_IER, port->ier); } static void sdio_uart_stop_tx(struct sdio_uart_port *port) if (port->ier & UART_IER_THRI) { sdio_out(port, UART_IER, port->ier); } static void sdio_uart_stop_rx(struct sdio_uart_port *port) port->ier &= ~UART_IER_RLSI; sdio_out(port, UART_IER, port->ier); unsigned int *status) struct tty_struct *tty = tty_port_tty_get(&port->port); int max_count = 256; do { flag = TTY_NORMAL; UART_LSR_FE | UART_LSR_OE))) { * For statistics only if (*status & UART_LSR_BI) { port->icount.brk++; port->icount.parity++; port->icount.frame++; port->icount.overrun++; /* */ if (*status & UART_LSR_BI) else if (*status & UART_LSR_PE) else if (*status & UART_LSR_FE) } if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0) tty_insert_flip_char(tty, ch, flag); /* * it doesn't affect the current character. if (*status & ~port->ignore_status_mask & UART_LSR_OE) tty_insert_flip_char(tty, 0, TTY_OVERRUN); *status = sdio_in(port, UART_LSR); if (tty) { tty_kref_put(tty); } static void sdio_uart_transmit_chars(struct sdio_uart_port *port) struct kfifo *xmit = &port->xmit_fifo; struct tty_struct *tty; int len; if (port->x_char) { port->icount.tx++; return; tty->stopped || tty->hw_stopped) { tty_kref_put(tty); } len = kfifo_out_locked(xmit, iobuf, 16, &port->write_lock); sdio_out(port, UART_TX, iobuf[count]); } len = kfifo_len(xmit); tty_wakeup(tty); sdio_uart_stop_tx(port); tty_kref_put(tty); { struct tty_struct *tty; status = sdio_in(port, UART_MSR); if ((status & UART_MSR_ANY_DELTA) == 0) port->icount.rng++; port->icount.dsr++; port->icount.dcd++; if (status & UART_MSR_DCD) else { tty = tty_port_tty_get(&port->port); tty_hangup(tty); } } port->icount.cts++; if (tty && (tty->termios->c_cflag & CRTSCTS)) { if (tty->hw_stopped) { tty->hw_stopped = 0; tty_wakeup(tty); } else { tty->hw_stopped = 1; } } } * This handles the interrupt from one port. static void sdio_uart_irq(struct sdio_func *func) struct sdio_uart_port *port = sdio_get_drvdata(func); * In a few places sdio_uart_irq() is called directly instead of * thread scheduled in order to reduce latency. However, some * (serial echo, flow control, etc.) through those same places */ return; iir = sdio_in(port, UART_IIR); return; port->in_sdio_uart_irq = current; if (lsr & UART_LSR_DR) sdio_uart_check_modem_status(port); sdio_uart_transmit_chars(port); } static int uart_carrier_raised(struct tty_port *tport) struct sdio_uart_port *port = unsigned int ret = sdio_uart_claim_func(port); return 1; sdio_uart_release_func(port); return 1; } /** * @tport: tty port to be updated * * adjusted during an open, close and hangup. { container_of(tport, struct sdio_uart_port, port); if (ret) if (onoff == 0) else sdio_uart_release_func(port); * sdio_uart_activate - start up hardware * @tty: tty bound to this port * Activate a tty port. The port locking guarantees us this will be * shutdown method run exactly once to match. Start up and shutdown are * at the same time even during a hangup event. * If we successfully start up the port we take an extra kref as we */ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty) struct sdio_uart_port *port = int ret; /* * once we have successfully opened the port. set_bit(TTY_IO_ERROR, &tty->flags); kfifo_reset(&port->xmit_fifo); ret = sdio_uart_claim_func(port); return ret; if (ret) ret = sdio_claim_irq(port->func, sdio_uart_irq); goto err2; /* * (they will be reenabled in sdio_change_speed()) sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO); UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); * Clear the interrupt registers. (void) sdio_in(port, UART_LSR); (void) sdio_in(port, UART_IIR); * 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; sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); if (tty->termios->c_cflag & CRTSCTS) tty->hw_stopped = 1; clear_bit(TTY_IO_ERROR, &tty->flags); /* Kick the IRQ handler once while we're still holding the host lock */ return 0; err2: err1: return ret; * sdio_uart_shutdown - stop hardware * * run only if a successful matching activate already ran. The two are * at the same time even during a hangup event. { container_of(tport, struct sdio_uart_port, port); if (ret) sdio_release_irq(port->func); sdio_out(port, UART_IER, 0); sdio_uart_clear_mctrl(port, TIOCM_OUT2); /* Disable break condition and FIFOs. */ sdio_out(port, UART_LCR, port->lcr); UART_FCR_CLEAR_RCVR | sdio_out(port, UART_FCR, 0); sdio_disable_func(port->func); sdio_uart_release_func(port); * sdio_uart_install - install method * @tty: the tty being bound * Look up and bind the tty and the driver together. Initialize */ static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty) int idx = tty->index; int ret = tty_standard_install(driver, tty); if (ret == 0) tty->driver_data = port; sdio_uart_port_put(port); } /** * @tty: the tty being destroyed * Called asynchronously when the last reference to the tty is dropped. */ static void sdio_uart_cleanup(struct tty_struct *tty) struct sdio_uart_port *port = tty->driver_data; sdio_uart_port_put(port); * Open/close/hangup is now entirely boilerplate { return tty_port_open(&port->port, tty, filp); { tty_port_close(&port->port, tty, filp); { tty_port_hangup(&port->port); int count) struct sdio_uart_port *port = tty->driver_data; return -ENODEV; ret = kfifo_in_locked(&port->xmit_fifo, buf, count, &port->write_lock); int err = sdio_uart_claim_func(port); sdio_uart_start_tx(port); sdio_uart_release_func(port); ret = err; } static int sdio_uart_write_room(struct tty_struct *tty) struct sdio_uart_port *port = tty->driver_data; } static int sdio_uart_chars_in_buffer(struct tty_struct *tty) struct sdio_uart_port *port = tty->driver_data; } 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 (sdio_uart_claim_func(port) != 0) sdio_uart_start_tx(port); 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 (I_IXOFF(tty)) { sdio_uart_start_tx(port); sdio_uart_clear_mctrl(port, TIOCM_RTS); sdio_uart_irq(port->func); } 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 (I_IXOFF(tty)) { port->x_char = 0; port->x_char = START_CHAR(tty); } sdio_uart_set_mctrl(port, TIOCM_RTS); sdio_uart_irq(port->func); } static void sdio_uart_set_termios(struct tty_struct *tty, { unsigned int cflag = tty->termios->c_cflag; if (sdio_uart_claim_func(port) != 0) if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) sdio_uart_set_mctrl(port, mask); if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { sdio_uart_start_tx(port); if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { tty->hw_stopped = 1; } } static int sdio_uart_break_ctl(struct tty_struct *tty, int break_state) struct sdio_uart_port *port = tty->driver_data; if (result != 0) port->lcr |= UART_LCR_SBC; port->lcr &= ~UART_LCR_SBC; return 0; { int result; result = sdio_uart_claim_func(port); result = port->mctrl | sdio_uart_get_mctrl(port); } return result; unsigned int set, unsigned int clear) struct sdio_uart_port *port = tty->driver_data; if (!result) { sdio_uart_release_func(port); } static int sdio_uart_proc_show(struct seq_file *m, void *v) int i; seq_printf(m, "serinfo:1.0 driver%s%s revision:%sn", for (i = 0; i < UART_NR; i++) { if (port) { if (capable(CAP_SYS_ADMIN)) { port->icount.tx, port->icount.rx); seq_printf(m, " fe:%d", if (port->icount.parity) port->icount.parity); seq_printf(m, " brk:%d", if (port->icount.overrun) port->icount.overrun); seq_printf(m, " cts:%d", if (port->icount.dsr) port->icount.dsr); seq_printf(m, " rng:%d", if (port->icount.dcd) port->icount.dcd); sdio_uart_port_put(port); } return 0; { } static const struct file_operations sdio_uart_proc_fops = { .open = sdio_uart_proc_open, .llseek = seq_lseek, }; static const struct tty_port_operations sdio_uart_port_ops = { .carrier_raised = uart_carrier_raised, .activate = sdio_uart_activate, .open = sdio_uart_open, .write = sdio_uart_write, .chars_in_buffer = sdio_uart_chars_in_buffer, .throttle = sdio_uart_throttle, .set_termios = sdio_uart_set_termios, .break_ctl = sdio_uart_break_ctl, .tiocmset = sdio_uart_tiocmset, .cleanup = sdio_uart_cleanup, }; static struct tty_driver *sdio_uart_tty_driver; static int sdio_uart_probe(struct sdio_func *func, { int ret; port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL); return -ENOMEM; if (func->class == SDIO_CLASS_UART) { sdio_func_id(func)); return -ENOSYS; /* * and SUBTPL_RCVCAPS. for (tpl = func->tuples; tpl; tpl = tpl->next) { continue; continue; break; if (!tpl) { "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS classn", kfree(port); } sdio_func_id(func), tpl->data[2], tpl->data[3]); (tpl->data[5] << 8) | pr_debug("%s: regs offset = 0x%xn", port->uartclk = tpl->data[7] * 115200; port->uartclk = 115200; sdio_func_id(func), port->uartclk, } else { return -EINVAL; sdio_set_drvdata(func, port); port->port.ops = &sdio_uart_port_ops; ret = sdio_uart_add_port(port); kfree(port); struct device *dev; port->index, &func->dev); sdio_uart_port_remove(port); } } 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); } static const struct sdio_device_id sdio_uart_ids[] = { { SDIO_DEVICE_CLASS(SDIO_CLASS_GPS) }, }; MODULE_DEVICE_TABLE(sdio, sdio_uart_ids); static struct sdio_driver sdio_uart_driver = { .remove = sdio_uart_remove, .id_table = sdio_uart_ids, { struct tty_driver *tty_drv; sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR); return -ENOMEM; tty_drv->driver_name = "sdio_uart"; tty_drv->major = 0; /* dynamically allocated */ tty_drv->type = TTY_DRIVER_TYPE_SERIAL; tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; tty_drv->init_termios.c_ospeed = 4800; if (ret) if (ret) tty_unregister_driver(tty_drv); put_tty_driver(tty_drv); } static void __exit sdio_uart_exit(void) sdio_unregister_driver(&sdio_uart_driver); put_tty_driver(sdio_uart_tty_driver); module_exit(sdio_uart_exit); MODULE_AUTHOR("Nicolas Pitre"); MODULE_LICENSE("GPL"); |
|
相关推荐
1个回答
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
753 浏览 0 评论
AI模型部署边缘设备的奇妙之旅:如何在边缘端部署OpenCV
2714 浏览 0 评论
tms320280021 adc采样波形,为什么adc采样频率上来波形就不好了?
1368 浏览 0 评论
1955 浏览 0 评论
1523 浏览 0 评论
74989 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 10:36 , Processed in 0.772124 second(s), Total 76, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号