ser-gigaset.c
来自「linux 内核源代码」· C语言 代码 · 共 838 行 · 第 1/2 页
C
838 行
return 0; } dev_set_drvdata(&cs->hw.ser->dev.dev, cs); tasklet_init(&cs->write_tasklet, &gigaset_modem_fill, (unsigned long) cs); return 1;}/* * set modem control lines * Parameters: * card state structure * modem control line state ([TIOCM_DTR]|[TIOCM_RTS]) * Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c * and by "if_lock" and "if_termios" in interface.c */static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, unsigned new_state){ struct tty_struct *tty = cs->hw.ser->tty; unsigned int set, clear; if (!tty || !tty->driver || !tty->driver->tiocmset) return -EFAULT; set = new_state & ~old_state; clear = old_state & ~new_state; if (!set && !clear) return 0; gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear); return tty->driver->tiocmset(tty, NULL, set, clear);}static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag){ return -EINVAL;}static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag){ return -EINVAL;}static const struct gigaset_ops ops = { gigaset_write_cmd, gigaset_write_room, gigaset_chars_in_buffer, gigaset_brkchars, gigaset_init_bchannel, gigaset_close_bchannel, gigaset_initbcshw, gigaset_freebcshw, gigaset_reinitbcshw, gigaset_initcshw, gigaset_freecshw, gigaset_set_modem_ctrl, gigaset_baud_rate, gigaset_set_line_ctrl, gigaset_m10x_send_skb, /* asyncdata.c */ gigaset_m10x_input, /* asyncdata.c */};/* Line Discipline Interface *//* ========================= *//* helper functions for cardstate refcounting */static struct cardstate *cs_get(struct tty_struct *tty){ struct cardstate *cs = tty->disc_data; if (!cs || !cs->hw.ser) { gig_dbg(DEBUG_ANY, "%s: no cardstate", __func__); return NULL; } atomic_inc(&cs->hw.ser->refcnt); return cs;}static void cs_put(struct cardstate *cs){ if (atomic_dec_and_test(&cs->hw.ser->refcnt)) mutex_unlock(&cs->hw.ser->dead_mutex);}/* * Called by the tty driver when the line discipline is pushed onto the tty. * Called in process context. */static intgigaset_tty_open(struct tty_struct *tty){ struct cardstate *cs; gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101"); info(DRIVER_AUTHOR); info(DRIVER_DESC); if (!driver) { err("%s: no driver structure", __func__); return -ENODEV; } /* allocate memory for our device state and intialize it */ if (!(cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME))) goto error; cs->dev = &cs->hw.ser->dev.dev; cs->hw.ser->tty = tty; mutex_init(&cs->hw.ser->dead_mutex); atomic_set(&cs->hw.ser->refcnt, 1); tty->disc_data = cs; /* OK.. Initialization of the datastructures and the HW is done.. Now * startup system and notify the LL that we are ready to run */ if (startmode == SM_LOCKED) atomic_set(&cs->mstate, MS_LOCKED); if (!gigaset_start(cs)) { tasklet_kill(&cs->write_tasklet); goto error; } gig_dbg(DEBUG_INIT, "Startup of HLL done"); mutex_lock(&cs->hw.ser->dead_mutex); return 0;error: gig_dbg(DEBUG_INIT, "Startup of HLL failed"); tty->disc_data = NULL; gigaset_freecs(cs); return -ENODEV;}/* * Called by the tty driver when the line discipline is removed. * Called from process context. */static voidgigaset_tty_close(struct tty_struct *tty){ struct cardstate *cs = tty->disc_data; gig_dbg(DEBUG_INIT, "Stopping HLL for Gigaset M101"); if (!cs) { gig_dbg(DEBUG_INIT, "%s: no cardstate", __func__); return; } /* prevent other callers from entering ldisc methods */ tty->disc_data = NULL; if (!cs->hw.ser) err("%s: no hw cardstate", __func__); else { /* wait for running methods to finish */ if (!atomic_dec_and_test(&cs->hw.ser->refcnt)) mutex_lock(&cs->hw.ser->dead_mutex); } /* stop operations */ gigaset_stop(cs); tasklet_kill(&cs->write_tasklet); flush_send_queue(cs); cs->dev = NULL; gigaset_freecs(cs); gig_dbg(DEBUG_INIT, "Shutdown of HLL done");}/* * Called by the tty driver when the tty line is hung up. * Wait for I/O to driver to complete and unregister ISDN device. * This is already done by the close routine, so just call that. * Called from process context. */static int gigaset_tty_hangup(struct tty_struct *tty){ gigaset_tty_close(tty); return 0;}/* * Read on the tty. * Unused, received data goes only to the Gigaset driver. */static ssize_tgigaset_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t count){ return -EAGAIN;}/* * Write on the tty. * Unused, transmit data comes only from the Gigaset driver. */static ssize_tgigaset_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t count){ return -EAGAIN;}/* * Ioctl on the tty. * Called in process context only. * May be re-entered by multiple ioctl calling threads. */static intgigaset_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct cardstate *cs = cs_get(tty); int rc, val; int __user *p = (int __user *)arg; if (!cs) return -ENXIO; switch (cmd) { case TCGETS: case TCGETA: /* pass through to underlying serial device */ rc = n_tty_ioctl(tty, file, cmd, arg); break; case TCFLSH: /* flush our buffers and the serial port's buffer */ switch (arg) { case TCIFLUSH: /* no own input buffer to flush */ break; case TCIOFLUSH: case TCOFLUSH: flush_send_queue(cs); break; } /* flush the serial port's buffer */ rc = n_tty_ioctl(tty, file, cmd, arg); break; case FIONREAD: /* unused, always return zero */ val = 0; rc = put_user(val, p); break; default: rc = -ENOIOCTLCMD; } cs_put(cs); return rc;}/* * Poll on the tty. * Unused, always return zero. */static unsigned intgigaset_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait){ return 0;}/* * Called by the tty driver when a block of data has been received. * Will not be re-entered while running but other ldisc functions * may be called in parallel. * Can be called from hard interrupt level as well as soft interrupt * level or mainline. * Parameters: * tty tty structure * buf buffer containing received characters * cflags buffer containing error flags for received characters (ignored) * count number of received characters */static voidgigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count){ struct cardstate *cs = cs_get(tty); unsigned tail, head, n; struct inbuf_t *inbuf; if (!cs) return; if (!(inbuf = cs->inbuf)) { dev_err(cs->dev, "%s: no inbuf\n", __func__); cs_put(cs); return; } tail = atomic_read(&inbuf->tail); head = atomic_read(&inbuf->head); gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes", head, tail, count); if (head <= tail) { /* possible buffer wraparound */ n = min_t(unsigned, count, RBUFSIZE - tail); memcpy(inbuf->data + tail, buf, n); tail = (tail + n) % RBUFSIZE; buf += n; count -= n; } if (count > 0) { /* tail < head and some data left */ n = head - tail - 1; if (count > n) { dev_err(cs->dev, "inbuf overflow, discarding %d bytes\n", count - n); count = n; } memcpy(inbuf->data + tail, buf, count); tail += count; } gig_dbg(DEBUG_INTR, "setting tail to %u", tail); atomic_set(&inbuf->tail, tail); /* Everything was received .. Push data into handler */ gig_dbg(DEBUG_INTR, "%s-->BH", __func__); gigaset_schedule_event(cs); cs_put(cs);}/* * Called by the tty driver when there's room for more data to send. */static voidgigaset_tty_wakeup(struct tty_struct *tty){ struct cardstate *cs = cs_get(tty); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); if (!cs) return; tasklet_schedule(&cs->write_tasklet); cs_put(cs);}static struct tty_ldisc gigaset_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "ser_gigaset", .open = gigaset_tty_open, .close = gigaset_tty_close, .hangup = gigaset_tty_hangup, .read = gigaset_tty_read, .write = gigaset_tty_write, .ioctl = gigaset_tty_ioctl, .poll = gigaset_tty_poll, .receive_buf = gigaset_tty_receive, .write_wakeup = gigaset_tty_wakeup,};/* Initialization / Shutdown *//* ========================= */static int __init ser_gigaset_init(void){ int rc; gig_dbg(DEBUG_INIT, "%s", __func__); if ((rc = platform_driver_register(&device_driver)) != 0) { err("error %d registering platform driver", rc); return rc; } /* allocate memory for our driver state and intialize it */ if (!(driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, GIGASET_MODULENAME, GIGASET_DEVNAME, &ops, THIS_MODULE))) goto error; if ((rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc)) != 0) { err("error %d registering line discipline", rc); goto error; } return 0;error: if (driver) { gigaset_freedriver(driver); driver = NULL; } platform_driver_unregister(&device_driver); return rc;}static void __exit ser_gigaset_exit(void){ int rc; gig_dbg(DEBUG_INIT, "%s", __func__); if (driver) { gigaset_freedriver(driver); driver = NULL; } if ((rc = tty_unregister_ldisc(N_GIGASET_M101)) != 0) err("error %d unregistering line discipline", rc); platform_driver_unregister(&device_driver);}module_init(ser_gigaset_init);module_exit(ser_gigaset_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?