ser-gigaset.c

来自「linux 内核源代码」· C语言 代码 · 共 838 行 · 第 1/2 页

C
838
字号
/* This is the serial hardware link layer (HLL) for the Gigaset 307x isdn * DECT base (aka Sinus 45 isdn) using the RS232 DECT data module M101, * written as a line discipline. * * ===================================================================== * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== */#include "gigaset.h"#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/platform_device.h>#include <linux/tty.h>#include <linux/poll.h>/* Version Information */#define DRIVER_AUTHOR "Tilman Schmidt"#define DRIVER_DESC "Serial Driver for Gigaset 307x using Siemens M101"#define GIGASET_MINORS     1#define GIGASET_MINOR      0#define GIGASET_MODULENAME "ser_gigaset"#define GIGASET_DEVNAME    "ttyGS"/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */#define IF_WRITEBUF 264MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");MODULE_ALIAS_LDISC(N_GIGASET_M101);static int startmode = SM_ISDN;module_param(startmode, int, S_IRUGO);MODULE_PARM_DESC(startmode, "initial operation mode");static int cidmode = 1;module_param(cidmode, int, S_IRUGO);MODULE_PARM_DESC(cidmode, "stay in CID mode when idle");static struct gigaset_driver *driver;struct ser_cardstate {	struct platform_device	dev;	struct tty_struct	*tty;	atomic_t		refcnt;	struct mutex		dead_mutex;};static struct platform_driver device_driver = {	.driver = {		.name = GIGASET_MODULENAME,	},};static void flush_send_queue(struct cardstate *);/* transmit data from current open skb * result: number of bytes sent or error code < 0 */static int write_modem(struct cardstate *cs){	struct tty_struct *tty = cs->hw.ser->tty;	struct bc_state *bcs = &cs->bcs[0];	/* only one channel */	struct sk_buff *skb = bcs->tx_skb;	int sent;	if (!tty || !tty->driver || !skb)		return -EFAULT;	if (!skb->len) {		dev_kfree_skb_any(skb);		bcs->tx_skb = NULL;		return -EINVAL;	}	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);	sent = tty->driver->write(tty, skb->data, skb->len);	gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent);	if (sent < 0) {		/* error */		flush_send_queue(cs);		return sent;	}	skb_pull(skb, sent);	if (!skb->len) {		/* skb sent completely */		gigaset_skb_sent(bcs, skb);		gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",			(unsigned long) skb);		dev_kfree_skb_any(skb);		bcs->tx_skb = NULL;	}	return sent;}/* * transmit first queued command buffer * result: number of bytes sent or error code < 0 */static int send_cb(struct cardstate *cs){	struct tty_struct *tty = cs->hw.ser->tty;	struct cmdbuf_t *cb, *tcb;	unsigned long flags;	int sent = 0;	if (!tty || !tty->driver)		return -EFAULT;	cb = cs->cmdbuf;	if (!cb)		return 0;	/* nothing to do */	if (cb->len) {		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);		sent = tty->driver->write(tty, cb->buf + cb->offset, cb->len);		if (sent < 0) {			/* error */			gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent);			flush_send_queue(cs);			return sent;		}		cb->offset += sent;		cb->len -= sent;		gig_dbg(DEBUG_OUTPUT, "send_cb: sent %d, left %u, queued %u",			sent, cb->len, cs->cmdbytes);	}	while (cb && !cb->len) {		spin_lock_irqsave(&cs->cmdlock, flags);		cs->cmdbytes -= cs->curlen;		tcb = cb;		cs->cmdbuf = cb = cb->next;		if (cb) {			cb->prev = NULL;			cs->curlen = cb->len;		} else {			cs->lastcmdbuf = NULL;			cs->curlen = 0;		}		spin_unlock_irqrestore(&cs->cmdlock, flags);		if (tcb->wake_tasklet)			tasklet_schedule(tcb->wake_tasklet);		kfree(tcb);	}	return sent;}/* * send queue tasklet * If there is already a skb opened, put data to the transfer buffer * by calling "write_modem". * Otherwise take a new skb out of the queue. */static void gigaset_modem_fill(unsigned long data){	struct cardstate *cs = (struct cardstate *) data;	struct bc_state *bcs;	int sent = 0;	if (!cs || !(bcs = cs->bcs)) {		gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);		return;	}	if (!bcs->tx_skb) {		/* no skb is being sent; send command if any */		sent = send_cb(cs);		gig_dbg(DEBUG_OUTPUT, "%s: send_cb -> %d", __func__, sent);		if (sent)			/* something sent or error */			return;		/* no command to send; get skb */		if (!(bcs->tx_skb = skb_dequeue(&bcs->squeue)))			/* no skb either, nothing to do */			return;		gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)",			(unsigned long) bcs->tx_skb);	}	/* send skb */	gig_dbg(DEBUG_OUTPUT, "%s: tx_skb", __func__);	if (write_modem(cs) < 0)		gig_dbg(DEBUG_OUTPUT, "%s: write_modem failed", __func__);}/* * throw away all data queued for sending */static void flush_send_queue(struct cardstate *cs){	struct sk_buff *skb;	struct cmdbuf_t *cb;	unsigned long flags;	/* command queue */	spin_lock_irqsave(&cs->cmdlock, flags);	while ((cb = cs->cmdbuf) != NULL) {		cs->cmdbuf = cb->next;		if (cb->wake_tasklet)			tasklet_schedule(cb->wake_tasklet);		kfree(cb);	}	cs->cmdbuf = cs->lastcmdbuf = NULL;	cs->cmdbytes = cs->curlen = 0;	spin_unlock_irqrestore(&cs->cmdlock, flags);	/* data queue */	if (cs->bcs->tx_skb)		dev_kfree_skb_any(cs->bcs->tx_skb);	while ((skb = skb_dequeue(&cs->bcs->squeue)) != NULL)		dev_kfree_skb_any(skb);}/* Gigaset Driver Interface *//* ======================== *//* * queue an AT command string for transmission to the Gigaset device * parameters: *	cs		controller state structure *	buf		buffer containing the string to send *	len		number of characters to send *	wake_tasklet	tasklet to run when transmission is complete, or NULL * return value: *	number of bytes queued, or error code < 0 */static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,                             int len, struct tasklet_struct *wake_tasklet){	struct cmdbuf_t *cb;	unsigned long flags;	gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ?	                     DEBUG_TRANSCMD : DEBUG_LOCKCMD,	                   "CMD Transmit", len, buf);	if (len <= 0)		return 0;	if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {		dev_err(cs->dev, "%s: out of memory!\n", __func__);		return -ENOMEM;	}	memcpy(cb->buf, buf, len);	cb->len = len;	cb->offset = 0;	cb->next = NULL;	cb->wake_tasklet = wake_tasklet;	spin_lock_irqsave(&cs->cmdlock, flags);	cb->prev = cs->lastcmdbuf;	if (cs->lastcmdbuf)		cs->lastcmdbuf->next = cb;	else {		cs->cmdbuf = cb;		cs->curlen = len;	}	cs->cmdbytes += len;	cs->lastcmdbuf = cb;	spin_unlock_irqrestore(&cs->cmdlock, flags);	spin_lock_irqsave(&cs->lock, flags);	if (cs->connected)		tasklet_schedule(&cs->write_tasklet);	spin_unlock_irqrestore(&cs->lock, flags);	return len;}/* * tty_driver.write_room interface routine * return number of characters the driver will accept to be written * parameter: *	controller state structure * return value: *	number of characters */static int gigaset_write_room(struct cardstate *cs){	unsigned bytes;	bytes = cs->cmdbytes;	return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;}/* * tty_driver.chars_in_buffer interface routine * return number of characters waiting to be sent * parameter: *	controller state structure * return value: *	number of characters */static int gigaset_chars_in_buffer(struct cardstate *cs){	return cs->cmdbytes;}/* * implementation of ioctl(GIGASET_BRKCHARS) * parameter: *	controller state structure * return value: *	-EINVAL (unimplemented function) */static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6]){	/* not implemented */	return -EINVAL;}/* * Open B channel * Called by "do_action" in ev-layer.c */static int gigaset_init_bchannel(struct bc_state *bcs){	/* nothing to do for M10x */	gigaset_bchannel_up(bcs);	return 0;}/* * Close B channel * Called by "do_action" in ev-layer.c */static int gigaset_close_bchannel(struct bc_state *bcs){	/* nothing to do for M10x */	gigaset_bchannel_down(bcs);	return 0;}/* * Set up B channel structure * This is called by "gigaset_initcs" in common.c */static int gigaset_initbcshw(struct bc_state *bcs){	/* unused */	bcs->hw.ser = NULL;	return 1;}/* * Free B channel structure * Called by "gigaset_freebcs" in common.c */static int gigaset_freebcshw(struct bc_state *bcs){	/* unused */	return 1;}/* * Reinitialize B channel structure * This is called by "bcs_reinit" in common.c */static void gigaset_reinitbcshw(struct bc_state *bcs){	/* nothing to do for M10x */}/* * Free hardware specific device data * This will be called by "gigaset_freecs" in common.c */static void gigaset_freecshw(struct cardstate *cs){	tasklet_kill(&cs->write_tasklet);	if (!cs->hw.ser)		return;	dev_set_drvdata(&cs->hw.ser->dev.dev, NULL);	platform_device_unregister(&cs->hw.ser->dev);	kfree(cs->hw.ser);	cs->hw.ser = NULL;}static void gigaset_device_release(struct device *dev){	struct platform_device *pdev =		container_of(dev, struct platform_device, dev);	/* adapted from platform_device_release() in drivers/base/platform.c */	//FIXME is this actually necessary?	kfree(dev->platform_data);	kfree(pdev->resource);}/* * Set up hardware specific device data * This is called by "gigaset_initcs" in common.c */static int gigaset_initcshw(struct cardstate *cs){	int rc;	if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) {		err("%s: out of memory!", __func__);		return 0;	}	cs->hw.ser->dev.name = GIGASET_MODULENAME;	cs->hw.ser->dev.id = cs->minor_index;	cs->hw.ser->dev.dev.release = gigaset_device_release;	if ((rc = platform_device_register(&cs->hw.ser->dev)) != 0) {		err("error %d registering platform device", rc);		kfree(cs->hw.ser);		cs->hw.ser = NULL;

⌨️ 快捷键说明

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