📄 16550a.c
字号:
rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); } if (testbits(config->config_mask, RTSER_SET_EVENT_MASK)) { /* change event mask atomically */ rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); ctx->config.event_mask = config->event_mask & EVENT_MASK; ctx->ioc_events = 0; if (testbits(config->event_mask, RTSER_EVENT_RXPEND) && (ctx->in_npend > 0)) ctx->ioc_events |= RTSER_EVENT_RXPEND; if (testbits(config->event_mask, RTSER_EVENT_ERRPEND) && ctx->status) ctx->ioc_events |= RTSER_EVENT_ERRPEND; if (testbits(config->event_mask, RTSER_EVENT_MODEMHI | RTSER_EVENT_MODEMLO)) /* enable modem status interrupt */ ctx->ier_status |= IER_TX; else /* disable modem status interrupt */ ctx->ier_status &= ~IER_TX; outb(ctx->ier_status, IER(ctx->dev_id)); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); } if (testbits(config->config_mask, RTSER_SET_HANDSHAKE)) { /* change handshake atomically */ rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); ctx->config.handshake = config->handshake; switch (ctx->config.handshake) { case RTSER_RTSCTS_HAND: // ...? default: /* RTSER_NO_HAND */ ctx->mcr_status = RTSER_MCR_DTR | RTSER_MCR_RTS | RTSER_MCR_OUT2; break; } outb(ctx->mcr_status, MCR(dev_id)); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); } return ret;}void rt_16550_cleanup_ctx(struct rt_16550_context *ctx){ rtdm_event_destroy(&ctx->in_event); rtdm_event_destroy(&ctx->out_event); rtdm_event_destroy(&ctx->ioc_event); rtdm_mutex_destroy(&ctx->out_lock);}int rt_16550_open(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, int oflags){ struct rt_16550_context *ctx; int dev_id = context->device->device_id; int ret; uint64_t *dummy; rtdm_lockctx_t lock_ctx; ctx = (struct rt_16550_context *)context->dev_private; /* IPC initialisation - cannot fail with used parameters */ rtdm_lock_init(&ctx->lock); rtdm_event_init(&ctx->in_event, 0); rtdm_event_init(&ctx->out_event, 0); rtdm_event_init(&ctx->ioc_event, 0); rtdm_mutex_init(&ctx->out_lock); ctx->dev_id = dev_id; ctx->in_head = 0; ctx->in_tail = 0; ctx->in_npend = 0; ctx->in_nwait = 0; ctx->in_lock = 0; ctx->in_history = NULL; ctx->out_head = 0; ctx->out_tail = 0; ctx->out_npend = 0; ctx->ioc_events = 0; ctx->ioc_event_lock = 0; ctx->status = 0; rt_16550_set_config(ctx, &default_config, &dummy); ret = rtdm_irq_request(&ctx->irq_handle, irq[dev_id], rt_16550_interrupt, RTDM_IRQTYPE_SHARED | RTDM_IRQTYPE_EDGE, context->device->proc_name, ctx); if (ret < 0) { /* reset DTR and RTS */ outb(0, MCR(dev_id)); rt_16550_cleanup_ctx(ctx); return ret; } rtdm_irq_enable(&ctx->irq_handle); rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); /* enable IRQ interrupts */ ctx->ier_status = IER_RX; outb(IER_RX, IER(dev_id)); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); return 0;}int rt_16550_close(struct rtdm_dev_context *context, rtdm_user_info_t *user_info){ struct rt_16550_context *ctx; int dev_id; uint64_t *in_history; rtdm_lockctx_t lock_ctx; ctx = (struct rt_16550_context *)context->dev_private; dev_id = ctx->dev_id; rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); /* reset DTR and RTS */ outb(0, MCR(dev_id)); /* mask all UART interrupts and clear pending ones. */ outb(0, IER(dev_id)); inb(IIR(dev_id)); inb(LSR(dev_id)); inb(RHR(dev_id)); inb(MSR(dev_id)); in_history = ctx->in_history; ctx->in_history = NULL; rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); /* We should disable the line now, but this requires counting * enable/disable, something not yet implemented by the core. * At the moment this call could starve other users sharing the * same line. rtdm_irq_disable(&ctx->irq_handle); */ rtdm_irq_free(&ctx->irq_handle); rt_16550_cleanup_ctx(ctx); if (in_history) { if (test_bit(RTDM_CREATED_IN_NRT, &context->context_flags)) kfree(in_history); else rtdm_free(in_history); } return 0;}int rt_16550_ioctl(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, int request, void *arg){ struct rt_16550_context *ctx; int ret = 0; int dev_id = context->device->device_id; ctx = (struct rt_16550_context *)context->dev_private; switch (request) { case RTSER_RTIOC_GET_CONFIG: if (user_info) { if (!rtdm_rw_user_ok(user_info, arg, sizeof(struct rtser_config)) || rtdm_copy_to_user(user_info, arg, &ctx->config, sizeof(struct rtser_config))) return -EFAULT; } else memcpy(arg, &ctx->config, sizeof(struct rtser_config)); break; case RTSER_RTIOC_SET_CONFIG: { struct rtser_config *config; struct rtser_config config_buf; uint64_t *hist_buf = NULL; config = (struct rtser_config *)arg; if (user_info) { if (!rtdm_read_user_ok(user_info, arg, sizeof(struct rtser_config)) || rtdm_copy_from_user(user_info, &config_buf, arg, sizeof(struct rtser_config))) return -EFAULT; config = &config_buf; } if (testbits(config->config_mask, RTSER_SET_BAUD) && (config->baud_rate > baud_base[dev_id])) { /* the baudrate is to high for this port */ return -EINVAL; } if (testbits(config->config_mask, RTSER_SET_TIMESTAMP_HISTORY)) { if (test_bit(RTDM_CREATED_IN_NRT, &context->context_flags) && rtdm_in_rt_context()) { /* already fail if we MAY allocate or release a non-RT * buffer in RT context */ return -EPERM; } if (testbits(config->timestamp_history, RTSER_RX_TIMESTAMP_HISTORY)) { if (test_bit(RTDM_CREATED_IN_NRT, &context->context_flags)) hist_buf = kmalloc(IN_BUFFER_SIZE * sizeof(uint64_t), GFP_KERNEL); else hist_buf = rtdm_malloc(IN_BUFFER_SIZE * sizeof(uint64_t)); if (!hist_buf) return -ENOMEM; } } rt_16550_set_config(ctx, config, &hist_buf); if (hist_buf) { if (test_bit(RTDM_CREATED_IN_NRT, &context->context_flags)) kfree(hist_buf); else rtdm_free(hist_buf); } break; } case RTSER_RTIOC_GET_STATUS: { rtdm_lockctx_t lock_ctx; if (user_info) { struct rtser_status status_buf; status_buf.line_status = inb(LSR(ctx->dev_id)); status_buf.modem_status = inb(MSR(ctx->dev_id)); if (!rtdm_rw_user_ok(user_info, arg, sizeof(struct rtser_status)) || rtdm_copy_to_user(user_info, arg, &status_buf, sizeof(struct rtser_status))) return -EFAULT; } else { ((struct rtser_status *)arg)->line_status = inb(LSR(ctx->dev_id)); ((struct rtser_status *)arg)->modem_status = inb(MSR(ctx->dev_id)); } rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); ctx->status = 0; ctx->ioc_events &= ~RTSER_EVENT_ERRPEND; rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); break; } case RTSER_RTIOC_GET_CONTROL: if (user_info) { if (!rtdm_rw_user_ok(user_info, arg, sizeof(int)) || rtdm_copy_to_user(user_info, arg, &ctx->mcr_status, sizeof(int))) ret = -EFAULT; } else *(int *)arg = ctx->mcr_status; break; case RTSER_RTIOC_SET_CONTROL: { long new_mcr; rtdm_lockctx_t lock_ctx; new_mcr = (long)arg; rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); ctx->mcr_status = new_mcr; outb(new_mcr, MCR(ctx->dev_id)); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); break; } case RTSER_RTIOC_WAIT_EVENT: { struct rtser_event ev = { rxpend_timestamp: 0 }; rtdm_lockctx_t lock_ctx; rtdm_toseq_t timeout_seq; if (!rtdm_in_rt_context()) return -ENOSYS; /* only one waiter allowed, stop any further attempts here */ if (test_and_set_bit(0, &ctx->ioc_event_lock)) return -EBUSY; rtdm_toseq_init(&timeout_seq, ctx->config.event_timeout); rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); while (!ctx->ioc_events) { /* enable error interrupt only when the user waits for it */ if (testbits(ctx->config.event_mask, RTSER_EVENT_ERRPEND)) { ctx->ier_status |= IER_STAT; outb(ctx->ier_status, IER(ctx->dev_id)); } rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); ret = rtdm_event_timedwait(&ctx->ioc_event, ctx->config.event_timeout, &timeout_seq); if (ret < 0) { clear_bit(0, &ctx->ioc_event_lock); if (ret == -EIDRM) /* device has been closed */ ret = -EBADF; goto wait_unlock_out; } rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); } ev.events = ctx->ioc_events; ctx->ioc_events &= ~(RTSER_EVENT_MODEMHI | RTSER_EVENT_MODEMLO); ev.last_timestamp = ctx->last_timestamp; ev.rx_pending = ctx->in_npend; if (ctx->in_history) ev.rxpend_timestamp = ctx->in_history[ctx->in_head]; rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); if (user_info) { if (!rtdm_rw_user_ok(user_info, arg, sizeof(struct rtser_event)) || rtdm_copy_to_user(user_info, arg, &ev, sizeof(struct rtser_event))) ret = -EFAULT; } else memcpy(arg, &ev, sizeof(struct rtser_event)); wait_unlock_out: /* release the simple event waiter lock */ clear_bit(0, &ctx->ioc_event_lock); break; } default: ret = -ENOTTY; } return ret;}ssize_t rt_16550_read(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, void *buf, size_t nbyte){ struct rt_16550_context *ctx; int dev_id; rtdm_lockctx_t lock_ctx;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -