⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 auerchar.c

📁 ep9315平台下USB驱动的源码
💻 C
字号:
/*****************************************************************************//* *      auerchar.c  --  Auerswald PBX/System Telephone character interface. * *      Copyright (C) 2002  Wolfgang M黣s (wolfgang@iksw-muees.de) * *      Very much code of this driver is borrowed from dabusb.c (Deti Fliegl) *      and from the USB Skeleton driver (Greg Kroah-Hartman). Thank you. * *      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. * *      This program is distributed in the hope that it will be useful, *      but WITHOUT ANY WARRANTY; without even the implied warranty of *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *      GNU General Public License for more details. * *      You should have received a copy of the GNU General Public License *      along with this program; if not, write to the Free Software *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /*****************************************************************************/#undef DEBUG			/* include debug macros until it's done */#include <linux/usb.h>#include "auerchar.h"#include "auermain.h"#include <linux/slab.h>#include <asm/uaccess.h>	/* user area access functions *//*-------------------------------------------------------------------*//* wake up waiting readers */static void auerchar_disconnect(struct auerscon *scp){	struct auerchar *ccp =((struct auerchar *) ((char *) (scp) - (unsigned long) (&((struct auerchar *) 0)->scontext)));	dbg("auerchar_disconnect called");	ccp->removed = 1;	wake_up(&ccp->readwait);}/* dispatch a read paket to a waiting character device */static void auerchar_ctrlread_dispatch(struct auerscon *scp,				       struct auerbuf *bp){	unsigned long flags;	struct auerchar *ccp;	struct auerbuf *newbp = NULL;	char *charp;	dbg("auerchar_ctrlread_dispatch called");	ccp =((struct auerchar *) ((char *) (scp) - (unsigned long)(&((struct auerchar *) 0)->scontext)));	/* get a read buffer from character device context */	newbp = auerbuf_getbuf(&ccp->bufctl);	if (!newbp) {		dbg("No read buffer available, discard paket!");		return;		/* no buffer, no dispatch */	}	/* copy information to new buffer element	   (all buffers have the same length) */	charp = newbp->bufp;	newbp->bufp = bp->bufp;	bp->bufp = charp;	newbp->len = bp->len;	/* insert new buffer in read list */	spin_lock_irqsave(&ccp->bufctl.lock, flags);	list_add_tail(&newbp->buff_list, &ccp->bufctl.rec_buff_list);	spin_unlock_irqrestore(&ccp->bufctl.lock, flags);	dbg("read buffer appended to rec_list");	/* wake up pending synchronous reads */	wake_up(&ccp->readwait);}/* Delete an auerswald character context */void auerchar_delete(struct auerchar *ccp){	dbg("auerchar_delete");	if (ccp == NULL)		return;	/* wake up pending synchronous reads */	ccp->removed = 1;	wake_up(&ccp->readwait);	/* remove the read buffer */	if (ccp->readbuf) {		auerbuf_releasebuf(ccp->readbuf);		ccp->readbuf = NULL;	}	/* remove the character buffers */	auerbuf_free_buffers(&ccp->bufctl);	/* release the memory */	kfree(ccp);}/* --------------------------------------------------------------------- *//* Char device functions                                                 *//* Open a new character device */int auerchar_open(struct inode *inode, struct file *file){	int dtindex = MINOR(inode->i_rdev) - AUER_MINOR_BASE;	struct auerswald *cp = NULL;	struct auerchar *ccp = NULL;	int ret;	/* minor number in range? */	if ((dtindex < 0) || (dtindex >= AUER_MAX_DEVICES)) {		return -ENODEV;	}	/* usb device available? */	if (down_interruptible(&auerdev_table_mutex)) {		return -ERESTARTSYS;	}	cp = auerdev_table[dtindex];	if (cp == NULL) {		up(&auerdev_table_mutex);		return -ENODEV;	}	if (down_interruptible(&cp->mutex)) {		up(&auerdev_table_mutex);		return -ERESTARTSYS;	}	up(&auerdev_table_mutex);	/* we have access to the device. Now lets allocate memory */	ccp = (struct auerchar *) kmalloc(sizeof(struct auerchar), GFP_KERNEL);	if (ccp == NULL) {		err("out of memory");		ret = -ENOMEM;		goto ofail;	}	/* Initialize device descriptor */	memset(ccp, 0, sizeof(struct auerchar));	init_MUTEX(&ccp->mutex);	init_MUTEX(&ccp->readmutex);	auerbuf_init(&ccp->bufctl);	ccp->scontext.id = AUH_UNASSIGNED;	ccp->scontext.dispatch = auerchar_ctrlread_dispatch;	ccp->scontext.disconnect = auerchar_disconnect;	init_waitqueue_head(&ccp->readwait);	ret =	    auerbuf_setup(&ccp->bufctl, AU_RBUFFERS,			  cp->maxControlLength + AUH_SIZE);	if (ret) {		goto ofail;	}	cp->open_count++;	ccp->auerdev = cp;	dbg("open %s as /dev/usb/%s", cp->dev_desc, cp->name);	up(&cp->mutex);	/* file IO stuff */	file->f_pos = 0;	file->private_data = ccp;	return 0;	/* Error exit */      ofail:up(&cp->mutex);	auerchar_delete(ccp);	return ret;}/* IOCTL functions */int auerchar_ioctl(struct inode *inode, struct file *file,		   unsigned int cmd, unsigned long arg){	struct auerchar *ccp = (struct auerchar *) file->private_data;	int ret = 0;	struct audevinfo devinfo;	struct auerswald *cp = NULL;	unsigned int u;	dbg("ioctl");	/* get the mutexes */	if (down_interruptible(&ccp->mutex)) {		return -ERESTARTSYS;	}	cp = ccp->auerdev;	if (!cp) {		up(&ccp->mutex);		return -ENODEV;	}	if (down_interruptible(&cp->mutex)) {		up(&ccp->mutex);		return -ERESTARTSYS;	}	/* Check for removal */	if (!cp->usbdev) {		up(&cp->mutex);		up(&ccp->mutex);		return -ENODEV;	}	switch (cmd) {		/* return != 0 if Transmitt channel ready to send */	case IOCTL_AU_TXREADY:		dbg("IOCTL_AU_TXREADY");		u = ccp->auerdev && (ccp->scontext.id != AUH_UNASSIGNED)		    && !list_empty(&cp->bufctl.free_buff_list);		ret = put_user(u, (unsigned int *) arg);		break;		/* return != 0 if connected to a service channel */	case IOCTL_AU_CONNECT:		dbg("IOCTL_AU_CONNECT");		u = (ccp->scontext.id != AUH_UNASSIGNED);		ret = put_user(u, (unsigned int *) arg);		break;		/* return != 0 if Receive Data available */	case IOCTL_AU_RXAVAIL:		dbg("IOCTL_AU_RXAVAIL");		if (ccp->scontext.id == AUH_UNASSIGNED) {			ret = -EIO;			break;		}		u = 0;		/* no data */		if (ccp->readbuf) {			int restlen = ccp->readbuf->len - ccp->readoffset;			if (restlen > 0)				u = 1;		}		if (!u) {			if (!list_empty(&ccp->bufctl.rec_buff_list)) {				u = 1;			}		}		ret = put_user(u, (unsigned int *) arg);		break;		/* return the max. buffer length for the device */	case IOCTL_AU_BUFLEN:		dbg("IOCTL_AU_BUFLEN");		u = cp->maxControlLength;		ret = put_user(u, (unsigned int *) arg);		break;		/* requesting a service channel */	case IOCTL_AU_SERVREQ:		dbg("IOCTL_AU_SERVREQ");		/* requesting a service means: release the previous one first */		auerswald_removeservice(cp, &ccp->scontext);		/* get the channel number */		ret = get_user(u, (unsigned int *) arg);		if (ret) {			break;		}		if ((u < AUH_FIRSTUSERCH) || (u >= AUH_TYPESIZE)) {			ret = -EIO;			break;		}		dbg("auerchar service request parameters are ok");		ccp->scontext.id = u;		/* request the service now */		ret = auerswald_addservice(cp, &ccp->scontext);		if (ret) {			/* no: revert service entry */			ccp->scontext.id = AUH_UNASSIGNED;		}		break;		/* get a string descriptor for the device */	case IOCTL_AU_DEVINFO:		dbg("IOCTL_AU_DEVINFO");		if (copy_from_user		    (&devinfo, (void *) arg, sizeof(struct audevinfo))) {			ret = -EFAULT;			break;		}		u = strlen(cp->dev_desc) + 1;		if (u > devinfo.bsize) {			u = devinfo.bsize;		}		ret = copy_to_user(devinfo.buf, cp->dev_desc, u);		break;		/* get the max. string descriptor length */	case IOCTL_AU_SLEN:		dbg("IOCTL_AU_SLEN");		u = AUSI_DLEN;		ret = put_user(u, (unsigned int *) arg);		break;	default:		dbg("IOCTL_AU_UNKNOWN");		ret = -ENOIOCTLCMD;		break;	}	/* release the mutexes */	up(&cp->mutex);	up(&ccp->mutex);	return ret;}/* Seek is not supported */loff_t auerchar_llseek(struct file * file, loff_t offset, int origin){	dbg("auerchar_seek");	return -ESPIPE;}/* Read data from the device */ssize_t auerchar_read(struct file * file, char *buf, size_t count,		      loff_t * ppos){	unsigned long flags;	struct auerchar *ccp = (struct auerchar *) file->private_data;	struct auerbuf *bp = NULL;	wait_queue_t wait;	dbg("auerchar_read");	/* Error checking */	if (!ccp)		return -EIO;	if (*ppos)		return -ESPIPE;	if (count == 0)		return 0;	/* get the mutex */	if (down_interruptible(&ccp->mutex))		return -ERESTARTSYS;	/* Can we expect to read something? */	if (ccp->scontext.id == AUH_UNASSIGNED) {		up(&ccp->mutex);		return -EIO;	}	/* only one reader per device allowed */	if (down_interruptible(&ccp->readmutex)) {		up(&ccp->mutex);		return -ERESTARTSYS;	}	/* read data from readbuf, if available */      doreadbuf:	bp = ccp->readbuf;	if (bp) {		/* read the maximum bytes */		int restlen = bp->len - ccp->readoffset;		if (restlen < 0)			restlen = 0;		if (count > restlen)			count = restlen;		if (count) {			if (copy_to_user			    (buf, bp->bufp + ccp->readoffset, count)) {				dbg("auerswald_read: copy_to_user failed");				up(&ccp->readmutex);				up(&ccp->mutex);				return -EFAULT;			}		}		/* advance the read offset */		ccp->readoffset += count;		restlen -= count;		// reuse the read buffer		if (restlen <= 0) {			auerbuf_releasebuf(bp);			ccp->readbuf = NULL;		}		/* return with number of bytes read */		if (count) {			up(&ccp->readmutex);			up(&ccp->mutex);			return count;		}	}	/* a read buffer is not available. Try to get the next data block. */      doreadlist:	/* Preparing for sleep */	init_waitqueue_entry(&wait, current);	set_current_state(TASK_INTERRUPTIBLE);	add_wait_queue(&ccp->readwait, &wait);	bp = NULL;	spin_lock_irqsave(&ccp->bufctl.lock, flags);	if (!list_empty(&ccp->bufctl.rec_buff_list)) {		/* yes: get the entry */		struct list_head *tmp = ccp->bufctl.rec_buff_list.next;		list_del(tmp);		bp = list_entry(tmp, struct auerbuf, buff_list);	}	spin_unlock_irqrestore(&ccp->bufctl.lock, flags);	/* have we got data? */	if (bp) {		ccp->readbuf = bp;		ccp->readoffset = AUH_SIZE;	/* for headerbyte */		set_current_state(TASK_RUNNING);		remove_wait_queue(&ccp->readwait, &wait);		goto doreadbuf;	/* now we can read! */	}	/* no data available. Should we wait? */	if (file->f_flags & O_NONBLOCK) {		dbg("No read buffer available, returning -EAGAIN");		set_current_state(TASK_RUNNING);		remove_wait_queue(&ccp->readwait, &wait);		up(&ccp->readmutex);		up(&ccp->mutex);		return -EAGAIN;	/* nonblocking, no data available */	}	/* yes, we should wait! */	up(&ccp->mutex);	/* allow other operations while we wait */	schedule();	remove_wait_queue(&ccp->readwait, &wait);	if (signal_pending(current)) {		/* waked up by a signal */		up(&ccp->readmutex);		return -ERESTARTSYS;	}	/* Anything left to read? */	if ((ccp->scontext.id == AUH_UNASSIGNED) || ccp->removed) {		up(&ccp->readmutex);		return -EIO;	}	if (down_interruptible(&ccp->mutex)) {		up(&ccp->readmutex);		return -ERESTARTSYS;	}	/* try to read the incomming data again */	goto doreadlist;}/* Write a data block into the right service channel of the device */ssize_t auerchar_write(struct file *file, const char *buf, size_t len,		       loff_t * ppos){	struct auerchar *ccp = (struct auerchar *) file->private_data;	struct auerswald *cp = NULL;	struct auerbuf *bp;	int ret;	wait_queue_t wait;	dbg("auerchar_write %d bytes", len);	/* Error checking */	if (!ccp)		return -EIO;	if (*ppos)		return -ESPIPE;	if (len == 0)		return 0;      write_again:	/* get the mutex */	if (down_interruptible(&ccp->mutex))		return -ERESTARTSYS;	/* Can we expect to write something? */	if (ccp->scontext.id == AUH_UNASSIGNED) {		up(&ccp->mutex);		return -EIO;	}	cp = ccp->auerdev;	if (!cp) {		up(&ccp->mutex);		return -ERESTARTSYS;	}	if (down_interruptible(&cp->mutex)) {		up(&ccp->mutex);		return -ERESTARTSYS;	}	if (!cp->usbdev) {		up(&cp->mutex);		up(&ccp->mutex);		return -EIO;	}	/* Prepare for sleep */	init_waitqueue_entry(&wait, current);	set_current_state(TASK_INTERRUPTIBLE);	add_wait_queue(&cp->bufferwait, &wait);	/* Try to get a buffer from the device pool.	   We can't use a buffer from ccp->bufctl because the write	   command will last beond a release() */	bp = auerbuf_getbuf(&cp->bufctl);	/* are there any buffers left? */	if (!bp) {		up(&cp->mutex);		up(&ccp->mutex);		/* NONBLOCK: don't wait */		if (file->f_flags & O_NONBLOCK) {			set_current_state(TASK_RUNNING);			remove_wait_queue(&cp->bufferwait, &wait);			return -EAGAIN;		}		/* BLOCKING: wait */		schedule();		remove_wait_queue(&cp->bufferwait, &wait);		if (signal_pending(current)) {			/* waked up by a signal */			return -ERESTARTSYS;		}		goto write_again;	} else {		set_current_state(TASK_RUNNING);		remove_wait_queue(&cp->bufferwait, &wait);	}	/* protect against too big write requests */	if (len > cp->maxControlLength)		len = cp->maxControlLength;	/* Fill the buffer */	if (copy_from_user(bp->bufp + AUH_SIZE, buf, len)) {		dbg("copy_from_user failed");		auerbuf_releasebuf(bp);		/* Wake up all processes waiting for a buffer */		wake_up(&cp->bufferwait);		up(&cp->mutex);		up(&ccp->mutex);		return -EIO;	}	/* set the header byte */	*(bp->bufp) = ccp->scontext.id | AUH_DIRECT | AUH_UNSPLIT;	/* Set the transfer Parameters */	bp->len = len + AUH_SIZE;	bp->dr->bRequestType = AUT_WREQ;	bp->dr->bRequest = AUV_WBLOCK;	bp->dr->wValue = cpu_to_le16(0);	bp->dr->wIndex =	    cpu_to_le16(ccp->scontext.id | AUH_DIRECT | AUH_UNSPLIT);	bp->dr->wLength = cpu_to_le16(len + AUH_SIZE);	FILL_CONTROL_URB(bp->urbp, cp->usbdev,			 usb_sndctrlpipe(cp->usbdev, 0),			 (unsigned char *) bp->dr, bp->bufp,			 len + AUH_SIZE, auerchar_ctrlwrite_complete, bp);	/* up we go */	ret = auerchain_submit_urb(&cp->controlchain, bp->urbp);	up(&cp->mutex);	if (ret) {		dbg("auerchar_write: nonzero result of auerchain_submit_urb %d", ret);		auerbuf_releasebuf(bp);		/* Wake up all processes waiting for a buffer */		wake_up(&cp->bufferwait);		up(&ccp->mutex);		return -EIO;	} else {		dbg("auerchar_write: Write OK");		up(&ccp->mutex);		return len;	}}/* Close a character device */int auerchar_release(struct inode *inode, struct file *file){	struct auerchar *ccp = (struct auerchar *) file->private_data;	struct auerswald *cp;	dbg("release");	/* get the mutexes */	if (down_interruptible(&ccp->mutex)) {		return -ERESTARTSYS;	}	cp = ccp->auerdev;	if (cp) {		if (down_interruptible(&cp->mutex)) {			up(&ccp->mutex);			return -ERESTARTSYS;		}		/* remove an open service */		auerswald_removeservice(cp, &ccp->scontext);		/* detach from device */		if ((--cp->open_count <= 0) && (cp->usbdev == NULL)) {			/* usb device waits for removal */			up(&cp->mutex);			auerswald_delete(cp);		} else {			up(&cp->mutex);		}		cp = NULL;		ccp->auerdev = NULL;	}	up(&ccp->mutex);	auerchar_delete(ccp);	return 0;}

⌨️ 快捷键说明

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