hfc_usb.c

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

C
1,609
字号
/* * hfc_usb.c * * $Id: hfc_usb.c,v 2.3.2.24 2007/10/14 08:40:29 mbachem Exp $ * * modular HiSax ISDN driver for Colognechip HFC-S USB chip * * Authors : Peter Sprenger (sprenger@moving-bytes.de) *           Martin Bachem (m.bachem@gmx.de, 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/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 <linux/moduleparam.h>#include "hisax.h"#include "hisax_if.h"#include "hfc_usb.h"static const char *hfcusb_revision =    "$Revision: 2.3.2.24 $ $Date: 2007/10/14 08:40:29 $ ";/* Hisax debug support*  debug flags defined in hfc_usb.h as HFCUSB_DBG_[*]*/#define __debug_variable hfc_debug#include "hisax_debug.h"static u_int debug;module_param(debug, uint, 0);static int hfc_debug;/* private vendor specific data */typedef struct {	__u8 led_scheme;	// led display scheme	signed short led_bits[8];	// array of 8 possible LED bitmask settings	char *vend_name;	// device name} hfcsusb_vdata;/* VID/PID device list */static struct usb_device_id hfcusb_idtab[] = {	{	 USB_DEVICE(0x0959, 0x2bd0),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_OFF, {4, 0, 2, 1},			   "ISDN USB TA (Cologne Chip HFC-S USB based)"}),	},	{	 USB_DEVICE(0x0675, 0x1688),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {1, 2, 0, 0},			   "DrayTek miniVigor 128 USB ISDN TA"}),	},	{	 USB_DEVICE(0x07b0, 0x0007),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {0x80, -64, -32, -16},			   "Billion tiny USB ISDN TA 128"}),	},	{	 USB_DEVICE(0x0742, 0x2008),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {4, 0, 2, 1},			   "Stollmann USB TA"}),	},	{	 USB_DEVICE(0x0742, 0x2009),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {4, 0, 2, 1},			   "Aceex USB ISDN TA"}),	},	{	 USB_DEVICE(0x0742, 0x200A),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {4, 0, 2, 1},			   "OEM USB ISDN TA"}),	},	{	 USB_DEVICE(0x08e3, 0x0301),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {2, 0, 1, 4},			   "Olitec USB RNIS"}),	},	{	 USB_DEVICE(0x07fa, 0x0846),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {0x80, -64, -32, -16},			   "Bewan Modem RNIS USB"}),	},	{	 USB_DEVICE(0x07fa, 0x0847),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {0x80, -64, -32, -16},			   "Djinn Numeris USB"}),	},	{	 USB_DEVICE(0x07b0, 0x0006),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {0x80, -64, -32, -16},			   "Twister ISDN TA"}),	},	{	 USB_DEVICE(0x071d, 0x1005),	 .driver_info = (unsigned long) &((hfcsusb_vdata)			  {LED_SCHEME1, {0x02, 0, 0x01, 0x04},			   "Eicon DIVA USB 4.0"}),	},	{ }};/* 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,	/* handles for control pipe */	    ctrl_out_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;	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 */} 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 "<unknown ERROR>";}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 =		    cpu_to_le16(hfc->ctrl_buff[hfc->ctrl_out_idx].hfc_reg);		hfc->ctrl_write.wValue =		    cpu_to_le16(hfc->ctrl_buff[hfc->ctrl_out_idx].reg_val);		usb_submit_urb(hfc->ctrl_urb, GFP_ATOMIC);	/* start transfer */	}}				/* ctrl_start_transfer */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);}static voidctrl_complete(struct urb *urb){	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];		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 */	}}/* 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);	}}static voidset_led_bit(hfcusb_data * hfc, signed short led_bits, int on){	if (on) {		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;	}}/* handle LED requests */static voidhandle_led(hfcusb_data * hfc, int event){	hfcsusb_vdata *driver_info =	    (hfcsusb_vdata *) hfcusb_idtab[hfc->vend_idx].driver_info;	/* if no scheme -> no LED action */	if (driver_info->led_scheme == LED_OFF)		return;	switch (event) {		case LED_POWER_ON:			set_led_bit(hfc, driver_info->led_bits[0], 1);			set_led_bit(hfc, driver_info->led_bits[1], 0);			set_led_bit(hfc, driver_info->led_bits[2], 0);			set_led_bit(hfc, driver_info->led_bits[3], 0);			break;		case LED_POWER_OFF:			set_led_bit(hfc, driver_info->led_bits[0], 0);			set_led_bit(hfc, driver_info->led_bits[1], 0);			set_led_bit(hfc, driver_info->led_bits[2], 0);			set_led_bit(hfc, driver_info->led_bits[3], 0);			break;		case LED_S0_ON:			set_led_bit(hfc, driver_info->led_bits[1], 1);			break;		case LED_S0_OFF:			set_led_bit(hfc, driver_info->led_bits[1], 0);			break;		case LED_B1_ON:			set_led_bit(hfc, driver_info->led_bits[2], 1);			break;		case LED_B1_OFF:			set_led_bit(hfc, driver_info->led_bits[2], 0);			break;		case LED_B2_ON:			set_led_bit(hfc, driver_info->led_bits[3], 1);			break;		case LED_B2_OFF:			set_led_bit(hfc, driver_info->led_bits[3], 0);			break;	}	write_led(hfc, hfc->led_state);}/* ISDN l1 timer T3 expires */static voidl1_timer_expire_t3(hfcusb_data * hfc){	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,			   NULL);	DBG(HFCUSB_DBG_STATES,	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T3 expire)");	hfc->l1_activated = 0;	handle_led(hfc, LED_S0_OFF);	/* deactivate : */	queue_control_request(hfc, HFCUSB_STATES, 0x10, 1);	queue_control_request(hfc, HFCUSB_STATES, 3, 1);}/* ISDN l1 timer T4 expires */static voidl1_timer_expire_t4(hfcusb_data * hfc){	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,			   NULL);	DBG(HFCUSB_DBG_STATES,	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T4 expire)");	hfc->l1_activated = 0;	handle_led(hfc, LED_S0_OFF);}/* S0 state changed */static voids0_state_handler(hfcusb_data * hfc, __u8 state){	__u8 old_state;	old_state = hfc->l1_state;	if (state == old_state || state < 1 || state > 8)		return;	DBG(HFCUSB_DBG_STATES, "HFC-S USB: S0 statechange(%d -> %d)",	    old_state, state);	if (state < 4 || state == 7 || state == 8) {		if (timer_pending(&hfc->t3_timer))			del_timer(&hfc->t3_timer);		DBG(HFCUSB_DBG_STATES, "HFC-S USB: T3 deactivated");	}	if (state >= 7) {		if (timer_pending(&hfc->t4_timer))			del_timer(&hfc->t4_timer);		DBG(HFCUSB_DBG_STATES, "HFC-S USB: T4 deactivated");	}	if (state == 7 && !hfc->l1_activated) {		hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,				   PH_ACTIVATE | INDICATION, NULL);		DBG(HFCUSB_DBG_STATES, "HFC-S USB: PH_ACTIVATE | INDICATION sent");		hfc->l1_activated = 1;		handle_led(hfc, LED_S0_ON);	} else if (state <= 3 /* && activated */ ) {		if (old_state == 7 || old_state == 8) {			DBG(HFCUSB_DBG_STATES, "HFC-S USB: T4 activated");			if (!timer_pending(&hfc->t4_timer)) {				hfc->t4_timer.expires =				    jiffies + (HFC_TIMER_T4 * HZ) / 1000;				add_timer(&hfc->t4_timer);			}		} else {			hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,					   PH_DEACTIVATE | INDICATION,					   NULL);			DBG(HFCUSB_DBG_STATES,			    "HFC-S USB: PH_DEACTIVATE | INDICATION sent");			hfc->l1_activated = 0;			handle_led(hfc, LED_S0_OFF);		}	}	hfc->l1_state = state;}static voidfill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,	      void *buf, int num_packets, int packet_size, int interval,	      usb_complete_t complete, void *context){	int k;	urb->dev = dev;	urb->pipe = pipe;	urb->complete = complete;	urb->number_of_packets = num_packets;	urb->transfer_buffer_length = packet_size * num_packets;	urb->context = context;	urb->transfer_buffer = buf;	urb->transfer_flags = URB_ISO_ASAP;	urb->actual_length = 0;	urb->interval = interval;	for (k = 0; k < num_packets; k++) {		urb->iso_frame_desc[k].offset = packet_size * k;		urb->iso_frame_desc[k].length = packet_size;		urb->iso_frame_desc[k].actual_length = 0;	}}/* allocs urbs and start isoc transfer with two pending urbs to avoid * gaps in the transfer chain */static intstart_isoc_chain(usb_fifo * fifo, int num_packets_per_urb,		 usb_complete_t complete, int packet_size){	int i, k, errcode;	DBG(HFCUSB_DBG_INIT, "HFC-S USB: starting ISO-URBs for fifo:%d\n",	    fifo->fifonum);	/* allocate Memory for Iso out Urbs */	for (i = 0; i < 2; i++) {		if (!(fifo->iso[i].purb)) {			fifo->iso[i].purb =			    usb_alloc_urb(num_packets_per_urb, GFP_KERNEL);			if (!(fifo->iso[i].purb)) {				printk(KERN_INFO				       "alloc urb for fifo %i failed!!!",				       fifo->fifonum);			}			fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo;			/* Init the first iso */			if (ISO_BUFFER_SIZE >=			    (fifo->usb_packet_maxlen *			     num_packets_per_urb)) {				fill_isoc_urb(fifo->iso[i].purb,					      fifo->hfc->dev, fifo->pipe,					      fifo->iso[i].buffer,					      num_packets_per_urb,					      fifo->usb_packet_maxlen,					      fifo->intervall, complete,					      &fifo->iso[i]);				memset(fifo->iso[i].buffer, 0,				       sizeof(fifo->iso[i].buffer));				/* defining packet delimeters in fifo->buffer */				for (k = 0; k < num_packets_per_urb; k++) {					fifo->iso[i].purb->					    iso_frame_desc[k].offset =					    k * packet_size;					fifo->iso[i].purb->					    iso_frame_desc[k].length =					    packet_size;				}			} else {				printk(KERN_INFO				       "HFC-S USB: ISO Buffer size to small!\n");			}		}		fifo->bit_line = BITLINE_INF;		errcode = usb_submit_urb(fifo->iso[i].purb, GFP_KERNEL);		fifo->active = (errcode >= 0) ? 1 : 0;		if (errcode < 0)			printk(KERN_INFO "HFC-S USB: usb_submit_urb URB nr:%d, error(%i): '%s'\n",			       i, errcode, symbolic(urb_errlist, errcode));	}	return (fifo->active);}/* stops running iso chain and frees their pending urbs */static voidstop_isoc_chain(usb_fifo * fifo){	int i;	for (i = 0; i < 2; i++) {		if (fifo->iso[i].purb) {			DBG(HFCUSB_DBG_INIT,			    "HFC-S USB: Stopping iso chain for fifo %i.%i",			    fifo->fifonum, i);			usb_kill_urb(fifo->iso[i].purb);			usb_free_urb(fifo->iso[i].purb);			fifo->iso[i].purb = NULL;		}	}	usb_kill_urb(fifo->urb);	usb_free_urb(fifo->urb);	fifo->urb = NULL;	fifo->active = 0;}/* defines how much ISO packets are handled in one URB */

⌨️ 快捷键说明

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