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

📄 sisusb.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles * * Main part * * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria * * If distributed as part of the Linux kernel, this code is licensed under the * terms of the GPL v2. * * Otherwise, the following license terms apply: * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1) Redistributions of source code must retain the above copyright * *    notice, this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright * *    notice, this list of conditions and the following disclaimer in the * *    documentation and/or other materials provided with the distribution. * * 3) The name of the author may not be used to endorse or promote products * *    derived from this software without specific psisusbr written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Author: 	Thomas Winischhofer <thomas@winischhofer.net> * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/kref.h>#include <linux/usb.h>#include <linux/smp_lock.h>#include <linux/vmalloc.h>#include "sisusb.h"#ifdef INCL_SISUSB_CON#include <linux/font.h>#endif#define SISUSB_DONTSYNC/* Forward declarations / clean-up routines */#ifdef INCL_SISUSB_CONint	sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);int	sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data);int	sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data);int	sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data);int	sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,	u8 myand, u8 myor);int	sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor);int	sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand);int	sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);int	sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data);int	sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data);int	sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data);int	sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,			u32 dest, int length, size_t *bytes_written);int	sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);extern int  SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);extern int  SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);extern void sisusb_init_concode(void);extern int  sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last);extern void sisusb_console_exit(struct sisusb_usb_data *sisusb);extern void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location);extern int  sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,		u8 *arg, int cmapsz, int ch512, int dorecalc,		struct vc_data *c, int fh, int uplock);static int sisusb_first_vc = 0;static int sisusb_last_vc = 0;module_param_named(first, sisusb_first_vc, int, 0);module_param_named(last, sisusb_last_vc, int, 0);MODULE_PARM_DESC(first, "Number of first console to take over (1 - MAX_NR_CONSOLES)");MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES)");#endifstatic struct usb_driver sisusb_driver;DECLARE_MUTEX(disconnect_sem);static voidsisusb_free_buffers(struct sisusb_usb_data *sisusb){	int i;	for (i = 0; i < NUMOBUFS; i++) {		if (sisusb->obuf[i]) {			usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize,				sisusb->obuf[i], sisusb->transfer_dma_out[i]);			sisusb->obuf[i] = NULL;		}	}	if (sisusb->ibuf) {		usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize,			sisusb->ibuf, sisusb->transfer_dma_in);		sisusb->ibuf = NULL;	}}static voidsisusb_free_urbs(struct sisusb_usb_data *sisusb){	int i;	for (i = 0; i < NUMOBUFS; i++) {		usb_free_urb(sisusb->sisurbout[i]);		sisusb->sisurbout[i] = NULL;	}	usb_free_urb(sisusb->sisurbin);	sisusb->sisurbin = NULL;}/* Level 0: USB transport layer *//* 1. out-bulks *//* out-urb management *//* Return 1 if all free, 0 otherwise */static intsisusb_all_free(struct sisusb_usb_data *sisusb){	int i;	for (i = 0; i < sisusb->numobufs; i++) {		if (sisusb->urbstatus[i] & SU_URB_BUSY)			return 0;	}	return 1;}/* Kill all busy URBs */static voidsisusb_kill_all_busy(struct sisusb_usb_data *sisusb){	int i;	if (sisusb_all_free(sisusb))		return;	for (i = 0; i < sisusb->numobufs; i++) {		if (sisusb->urbstatus[i] & SU_URB_BUSY)			usb_kill_urb(sisusb->sisurbout[i]);	}}/* Return 1 if ok, 0 if error (not all complete within timeout) */static intsisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb){	int timeout = 5 * HZ, i = 1;	wait_event_timeout(sisusb->wait_q,				(i = sisusb_all_free(sisusb)),				 timeout);	return i;}static intsisusb_outurb_available(struct sisusb_usb_data *sisusb){	int i;	for (i = 0; i < sisusb->numobufs; i++) {		if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)			return i;	}	return -1;}static intsisusb_get_free_outbuf(struct sisusb_usb_data *sisusb){	int i, timeout = 5 * HZ;	wait_event_timeout(sisusb->wait_q,				((i = sisusb_outurb_available(sisusb)) >= 0),				timeout);	return i;}static intsisusb_alloc_outbuf(struct sisusb_usb_data *sisusb){	int i;	i = sisusb_outurb_available(sisusb);	if (i >= 0)		sisusb->urbstatus[i] |= SU_URB_ALLOC;	return i;}static voidsisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index){	if ((index >= 0) && (index < sisusb->numobufs))		sisusb->urbstatus[index] &= ~SU_URB_ALLOC;}/* completion callback */static voidsisusb_bulk_completeout(struct urb *urb, struct pt_regs *regs){	struct sisusb_urb_context *context = urb->context;	struct sisusb_usb_data *sisusb;	if (!context)		return;	sisusb = context->sisusb;	if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)		return;#ifndef SISUSB_DONTSYNC	if (context->actual_length)		*(context->actual_length) += urb->actual_length;#endif	sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;	wake_up(&sisusb->wait_q);}static intsisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,		int len, int *actual_length, int timeout, unsigned int tflags,		dma_addr_t transfer_dma){	struct urb *urb = sisusb->sisurbout[index];	int retval, byteswritten = 0;	/* Set up URB */	urb->transfer_flags = 0;	usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,		sisusb_bulk_completeout, &sisusb->urbout_context[index]);	urb->transfer_flags |= tflags;	urb->actual_length = 0;	if ((urb->transfer_dma = transfer_dma))		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	/* Set up context */	sisusb->urbout_context[index].actual_length = (timeout) ?						NULL : actual_length;	/* Declare this urb/buffer in use */	sisusb->urbstatus[index] |= SU_URB_BUSY;	/* Submit URB */	retval = usb_submit_urb(urb, GFP_ATOMIC);	/* If OK, and if timeout > 0, wait for completion */	if ((retval == 0) && timeout) {		wait_event_timeout(sisusb->wait_q,				   (!(sisusb->urbstatus[index] & SU_URB_BUSY)),				   timeout);		if (sisusb->urbstatus[index] & SU_URB_BUSY) {			/* URB timed out... kill it and report error */			usb_kill_urb(urb);			retval = -ETIMEDOUT;		} else {			/* Otherwise, report urb status */			retval = urb->status;			byteswritten = urb->actual_length;		}	}	if (actual_length)		*actual_length = byteswritten;	return retval;}/* 2. in-bulks *//* completion callback */static voidsisusb_bulk_completein(struct urb *urb, struct pt_regs *regs){	struct sisusb_usb_data *sisusb = urb->context;	if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)		return;	sisusb->completein = 1;	wake_up(&sisusb->wait_q);}static intsisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len,		int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma){	struct urb *urb = sisusb->sisurbin;	int retval, readbytes = 0;	urb->transfer_flags = 0;	usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,			sisusb_bulk_completein, sisusb);	urb->transfer_flags |= tflags;	urb->actual_length = 0;	if ((urb->transfer_dma = transfer_dma))		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	sisusb->completein = 0;	retval = usb_submit_urb(urb, GFP_ATOMIC);	if (retval == 0) {		wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);		if (!sisusb->completein) {			/* URB timed out... kill it and report error */			usb_kill_urb(urb);			retval = -ETIMEDOUT;		} else {			/* URB completed within timout */			retval = urb->status;			readbytes = urb->actual_length;		}	}	if (actual_length)		*actual_length = readbytes;	return retval;}/* Level 1:  *//* Send a bulk message of variable size * * To copy the data from userspace, give pointer to "userbuffer", * to copy from (non-DMA) kernel memory, give "kernbuffer". If * both of these are NULL, it is assumed, that the transfer * buffer "sisusb->obuf[index]" is set up with the data to send. * Index is ignored if either kernbuffer or userbuffer is set. * If async is nonzero, URBs will be sent without waiting for * completion of the previous URB. * * (return 0 on success) */static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,		char *kernbuffer, const char __user *userbuffer, int index,		ssize_t *bytes_written, unsigned int tflags, int async){	int result = 0, retry, count = len;	int passsize, thispass, transferred_len = 0;	int fromuser = (userbuffer != NULL) ? 1 : 0;	int fromkern = (kernbuffer != NULL) ? 1 : 0;	unsigned int pipe;	char *buffer;	(*bytes_written) = 0;	/* Sanity check */	if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)		return -ENODEV;	/* If we copy data from kernel or userspace, force the	 * allocation of a buffer/urb. If we have the data in	 * the transfer buffer[index] already, reuse the buffer/URB	 * if the length is > buffer size. (So, transmitting	 * large data amounts directly from the transfer buffer	 * treats the buffer as a ring buffer. However, we need	 * to sync in this case.)	 */	if (fromuser || fromkern)		index = -1;	else if (len > sisusb->obufsize)		async = 0;	pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);	do {		passsize = thispass = (sisusb->obufsize < count) ?						sisusb->obufsize : count;		if (index < 0)			index = sisusb_get_free_outbuf(sisusb);		if (index < 0)			return -EIO;		buffer = sisusb->obuf[index];		if (fromuser) {			if (copy_from_user(buffer, userbuffer, passsize))				return -EFAULT;			userbuffer += passsize;		} else if (fromkern) {			memcpy(buffer, kernbuffer, passsize);			kernbuffer += passsize;		}		retry = 5;		while (thispass) {			if (!sisusb->sisusb_dev)				return -ENODEV;			result = sisusb_bulkout_msg(sisusb,						index,						pipe,						buffer,						thispass,						&transferred_len,						async ? 0 : 5 * HZ,						tflags,						sisusb->transfer_dma_out[index]);			if (result == -ETIMEDOUT) {				/* Will not happen if async */				if (!retry--)					return -ETIME;				continue;			} else if ((result == 0) && !async && transferred_len) {				thispass -= transferred_len;				if (thispass) {					if (sisusb->transfer_dma_out) {						/* If DMA, copy remaining						 * to beginning of buffer						 */						memcpy(buffer,						       buffer + transferred_len,						       thispass);					} else {						/* If not DMA, simply increase						 * the pointer						 */						buffer += transferred_len;					}				}			} else				break;		};		if (result)			return result;		(*bytes_written) += passsize;		count            -= passsize;		/* Force new allocation in next iteration */		if (fromuser || fromkern)			index = -1;	} while (count > 0);	if (async) {#ifdef SISUSB_DONTSYNC		(*bytes_written) = len;		/* Some URBs/buffers might be busy */#else		sisusb_wait_all_out_complete(sisusb);		(*bytes_written) = transferred_len;		/* All URBs and all buffers are available */#endif	}	return ((*bytes_written) == len) ? 0 : -EIO;}/* Receive a bulk message of variable size * * To copy the data to userspace, give pointer to "userbuffer", * to copy to kernel memory, give "kernbuffer". One of them * MUST be set. (There is no technique for letting the caller * read directly from the ibuf.) * */static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,		void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,		unsigned int tflags){	int result = 0, retry, count = len;	int bufsize, thispass, transferred_len;	unsigned int pipe;	char *buffer;	(*bytes_read) = 0;

⌨️ 快捷键说明

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