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 + -
显示快捷键?