hfc_usb.c

来自「底层驱动开发」· C语言 代码 · 共 1,829 行 · 第 1/4 页

C
1,829
字号
/* * hfc_usb.c * * $Id: hfc_usb.c,v 4.34 2005/01/26 17:25:53 martinb1 Exp $ * * modular HiSax ISDN driver for Colognechip HFC-S USB chip * * Authors : Peter Sprenger  (sprenger@moving-bytes.de) *           Martin Bachem   (info@colognechip.com) * *           based on the first hfc_usb driver of *           Werner Cornelius (werner@isdn-development.de) * * 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, 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. * * See Version Histroy at the bottom of this file **/#include <linux/types.h>#include <linux/stddef.h>#include <linux/timer.h>#include <linux/config.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kernel_stat.h>#include <linux/usb.h>#include <linux/kernel.h>#include <linux/smp_lock.h>#include <linux/sched.h>#include "hisax.h"#include "hisax_if.h"#include "hfc_usb.h"/** Version Information* (do not modify the CVS Makros $Revision: 4.34 $ and $Date: 2005/01/26 17:25:53 $ !)*/static const char *hfcusb_revision =    "Revision: 4.34 $ Date: 2005/01/26 17:25:53 $ ";/* Hisax debug support* use "modprobe debug=x" where x is bitfield of USB_DBG & ISDN_DBG*/#ifdef CONFIG_HISAX_DEBUG#include <linux/moduleparam.h>#define __debug_variable hfc_debug#include "hisax_debug.h"static u_int debug;module_param(debug, uint, 0);static int hfc_debug;#endif/****************************************//* data defining the devices to be used *//****************************************/static struct usb_device_id hfc_usb_idtab[] = {	{USB_DEVICE(0x0959, 0x2bd0)},	/* Colognechip USB eval TA */	{USB_DEVICE(0x0675, 0x1688)},	/* DrayTek miniVigor 128 USB ISDN TA */	{USB_DEVICE(0x07b0, 0x0007)},	/* Billion USB TA 2 */	{USB_DEVICE(0x0742, 0x2008)},	/* Stollmann USB TA */	{USB_DEVICE(0x0742, 0x2009)},	/* Aceex USB ISDN TA */	{USB_DEVICE(0x0742, 0x200A)},	/* OEM USB ISDN TA */	{USB_DEVICE(0x08e3, 0x0301)},	/* OliTec ISDN USB */	{USB_DEVICE(0x07fa, 0x0846)},	/* Bewan ISDN USB TA */	{USB_DEVICE(0x07fa, 0x0847)},	/* Djinn Numeris USB */	{USB_DEVICE(0x07b0, 0x0006)},	/* Twister ISDN USB TA */	{}			/* end with an all-zeroes entry */};/* driver internal device specific data:*   VendorID, ProductID, Devicename, LED_SCHEME,*   LED's BitMask in HFCUSB_P_DATA Register : LED_USB, LED_S0, LED_B1, LED_B2*/static vendor_data vdata[] = {	/* CologneChip Eval TA */	{0x0959, 0x2bd0, "ISDN USB TA (Cologne Chip HFC-S USB based)",	 LED_OFF, {4, 0, 2, 1}	 }	,	/* DrayTek miniVigor 128 USB ISDN TA */	{0x0675, 0x1688, "DrayTek miniVigor 128 USB ISDN TA",	 LED_SCHEME1, {1, 2, 0, 0}	 }	,	/* Billion TA */	{0x07b0, 0x0007, "Billion tiny USB ISDN TA 128",	 LED_SCHEME1, {0x80, -64, -32, -16}	 }	,	/* Stollmann TA */	{0x0742, 0x2008, "Stollmann USB TA",	 LED_SCHEME1, {4, 0, 2, 1}	 }	,	/* Aceex USB ISDN TA */	{0x0742, 0x2009, "Aceex USB ISDN TA",	 LED_SCHEME1, {4, 0, 2, 1}	 }	,	/* OEM USB ISDN TA */	{0x0742, 0x200A, "OEM USB ISDN TA",	 LED_SCHEME1, {4, 0, 2, 1}	 }	,	/* Olitec TA  */	{0x08e3, 0x0301, "Olitec USB RNIS",	 LED_SCHEME1, {2, 0, 1, 4}	 }	,	/* Bewan TA   */	{0x07fa, 0x0846, "Bewan Modem RNIS USB",	 LED_SCHEME1, {0x80, -64, -32, -16}	 }	,	/* Bewan TA   */	{0x07fa, 0x0847, "Djinn Numeris USB",	 LED_SCHEME1, {0x80, -64, -32, -16}	 }	,	/* Twister ISDN TA   */	{0x07b0, 0x0006, "Twister ISDN TA",	 LED_SCHEME1, {0x80, -64, -32, -16}	 }	,	{0, 0, 0}		/* EOL element */};/***************************************************************//* structure defining input+output fifos (interrupt/bulk mode) *//***************************************************************/struct usb_fifo;		/* forward definition */typedef struct iso_urb_struct {	struct urb *purb;	__u8 buffer[ISO_BUFFER_SIZE];	/* buffer incoming/outgoing data */	struct usb_fifo *owner_fifo;	/* pointer to owner fifo */} iso_urb_struct;struct hfcusb_data;		/* forward definition */typedef struct usb_fifo {	int fifonum;		/* fifo index attached to this structure */	int active;		/* fifo is currently active */	struct hfcusb_data *hfc;	/* pointer to main structure */	int pipe;		/* address of endpoint */	__u8 usb_packet_maxlen;	/* maximum length for usb transfer */	unsigned int max_size;	/* maximum size of receive/send packet */	__u8 intervall;		/* interrupt interval */	struct sk_buff *skbuff;	/* actual used buffer */	struct urb *urb;	/* transfer structure for usb routines */	__u8 buffer[128];	/* buffer incoming/outgoing data */	int bit_line;		/* how much bits are in the fifo? */	volatile __u8 usb_transfer_mode;	/* switched between ISO and INT */	iso_urb_struct iso[2];	/* need two urbs to have one always for pending */	struct hisax_if *hif;	/* hisax interface */	int delete_flg;		/* only delete skbuff once */	int last_urblen;	/* remember length of last packet */} usb_fifo;/*********************************************//* structure holding all data for one device *//*********************************************/typedef struct hfcusb_data {	/* HiSax Interface for loadable Layer1 drivers */	struct hisax_d_if d_if;	/* see hisax_if.h */	struct hisax_b_if b_if[2];	/* see hisax_if.h */	int protocol;	struct usb_device *dev;	/* our device */	int if_used;		/* used interface number */	int alt_used;		/* used alternate config */	int ctrl_paksize;	/* control pipe packet size */	int ctrl_in_pipe, ctrl_out_pipe;	/* handles for control pipe */	int cfg_used;		/* configuration index used */	int vend_idx;		/* vendor found */	int b_mode[2];		/* B-channel mode */	int l1_activated;	/* layer 1 activated */	int disc_flag;		/* TRUE if device was disonnected to avoid some USB actions */	int packet_size, iso_packet_size;	/* control pipe background handling */	ctrl_buft ctrl_buff[HFC_CTRL_BUFSIZE];	/* buffer holding queued data */	volatile int ctrl_in_idx, ctrl_out_idx, ctrl_cnt;	/* input/output pointer + count */	struct urb *ctrl_urb;	/* transfer structure for control channel */	struct usb_ctrlrequest ctrl_write;	/* buffer for control write request */	struct usb_ctrlrequest ctrl_read;	/* same for read request */	__u8 old_led_state, led_state, led_new_data, led_b_active;	volatile __u8 threshold_mask;	/* threshold actually reported */	volatile __u8 bch_enables;	/* or mask for sctrl_r and sctrl register values */	usb_fifo fifos[HFCUSB_NUM_FIFOS];	/* structure holding all fifo data */	volatile __u8 l1_state;	/* actual l1 state */	struct timer_list t3_timer;	/* timer 3 for activation/deactivation */	struct timer_list t4_timer;	/* timer 4 for activation/deactivation */	struct timer_list led_timer;	/* timer flashing leds */} hfcusb_data;static void collect_rx_frame(usb_fifo * fifo, __u8 * data, int len,			     int finish);static inline const char *symbolic(struct hfcusb_symbolic_list list[], const int num){	int i;	for (i = 0; list[i].name != NULL; i++)		if (list[i].num == num)			return (list[i].name);	return "<unkown>";}/******************************************************//* start next background transfer for control channel *//******************************************************/static voidctrl_start_transfer(hfcusb_data * hfc){	if (hfc->ctrl_cnt) {		hfc->ctrl_urb->pipe = hfc->ctrl_out_pipe;		hfc->ctrl_urb->setup_packet = (u_char *) & hfc->ctrl_write;		hfc->ctrl_urb->transfer_buffer = NULL;		hfc->ctrl_urb->transfer_buffer_length = 0;		hfc->ctrl_write.wIndex =		    hfc->ctrl_buff[hfc->ctrl_out_idx].hfc_reg;		hfc->ctrl_write.wValue =		    hfc->ctrl_buff[hfc->ctrl_out_idx].reg_val;		usb_submit_urb(hfc->ctrl_urb, GFP_ATOMIC);	/* start transfer */	}}				/* ctrl_start_transfer *//************************************//* queue a control transfer request *//* return 0 on success.             *//************************************/static intqueue_control_request(hfcusb_data * hfc, __u8 reg, __u8 val, int action){	ctrl_buft *buf;	if (hfc->ctrl_cnt >= HFC_CTRL_BUFSIZE)		return (1);	/* no space left */	buf = &hfc->ctrl_buff[hfc->ctrl_in_idx];	/* pointer to new index */	buf->hfc_reg = reg;	buf->reg_val = val;	buf->action = action;	if (++hfc->ctrl_in_idx >= HFC_CTRL_BUFSIZE)		hfc->ctrl_in_idx = 0;	/* pointer wrap */	if (++hfc->ctrl_cnt == 1)		ctrl_start_transfer(hfc);	return (0);}				/* queue_control_request */static intcontrol_action_handler(hfcusb_data * hfc, int reg, int val, int action){	if (!action)		return (1);	/* no action defined */	return (0);}/***************************************************************//* control completion routine handling background control cmds *//***************************************************************/static voidctrl_complete(struct urb *urb, struct pt_regs *regs){	hfcusb_data *hfc = (hfcusb_data *) urb->context;	ctrl_buft *buf;	urb->dev = hfc->dev;	if (hfc->ctrl_cnt) {		buf = &hfc->ctrl_buff[hfc->ctrl_out_idx];		control_action_handler(hfc, buf->hfc_reg, buf->reg_val,				       buf->action);		hfc->ctrl_cnt--;	/* decrement actual count */		if (++hfc->ctrl_out_idx >= HFC_CTRL_BUFSIZE)			hfc->ctrl_out_idx = 0;	/* pointer wrap */		ctrl_start_transfer(hfc);	/* start next transfer */	}}				/* ctrl_complete *//***************************************************//* write led data to auxport & invert if necessary *//***************************************************/static voidwrite_led(hfcusb_data * hfc, __u8 led_state){	if (led_state != hfc->old_led_state) {		hfc->old_led_state = led_state;		queue_control_request(hfc, HFCUSB_P_DATA, led_state, 1);	}}/**************************//* handle LED bits        *//**************************/static voidset_led_bit(hfcusb_data * hfc, signed short led_bits, int unset){	if (unset) {		if (led_bits < 0)			hfc->led_state |= abs(led_bits);		else			hfc->led_state &= ~led_bits;	} else {		if (led_bits < 0)			hfc->led_state &= ~abs(led_bits);		else			hfc->led_state |= led_bits;	}}/******************************************//* invert B-channel LEDs if data is sent  *//******************************************/static voidled_timer(hfcusb_data * hfc){	static int cnt = 0;	if (cnt) {		if (hfc->led_b_active & 1)			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],				    0);		if (hfc->led_b_active & 2)			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],				    0);	} else {		if (!(hfc->led_b_active & 1) || hfc->led_new_data & 1)			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],				    1);		if (!(hfc->led_b_active & 2) || hfc->led_new_data & 2)			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],				    1);	}	write_led(hfc, hfc->led_state);	hfc->led_new_data = 0;	cnt = !cnt;	/* restart 4 hz timer */	if (!timer_pending(&hfc->led_timer)) {		add_timer(&hfc->led_timer);		hfc->led_timer.expires = jiffies + (LED_TIME * HZ) / 1000;	}}/**************************//* handle LED requests    *//**************************/static voidhandle_led(hfcusb_data * hfc, int event){	/* if no scheme -> no LED action */	if (vdata[hfc->vend_idx].led_scheme == LED_OFF)		return;	switch (event) {		case LED_POWER_ON:			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[0],				    0);			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],				    1);			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],				    1);			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],				    1);			break;		case LED_POWER_OFF:	/* no Power off handling */			break;		case LED_S0_ON:			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],				    0);			break;		case LED_S0_OFF:			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],				    1);			break;		case LED_B1_ON:			hfc->led_b_active |= 1;			break;		case LED_B1_OFF:			hfc->led_b_active &= ~1;			break;		case LED_B1_DATA:			hfc->led_new_data |= 1;			break;		case LED_B2_ON:			hfc->led_b_active |= 2;			break;		case LED_B2_OFF:			hfc->led_b_active &= ~2;			break;		case LED_B2_DATA:			hfc->led_new_data |= 2;			break;	}	write_led(hfc, hfc->led_state);}/********************************//* called when timer t3 expires *//********************************/static voidl1_timer_expire_t3(hfcusb_data * hfc){	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,			   NULL);#ifdef CONFIG_HISAX_DEBUG	DBG(ISDN_DBG,	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T3 expire)");#endif	hfc->l1_activated = FALSE;	handle_led(hfc, LED_S0_OFF);	/* deactivate : */	queue_control_request(hfc, HFCUSB_STATES, 0x10, 1);	queue_control_request(hfc, HFCUSB_STATES, 3, 1);}/********************************//* called when timer t4 expires *//********************************/static voidl1_timer_expire_t4(hfcusb_data * hfc){	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,			   NULL);#ifdef CONFIG_HISAX_DEBUG	DBG(ISDN_DBG,	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T4 expire)");#endif

⌨️ 快捷键说明

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