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

📄 zjmdmdrv.c

📁 modem卡驱动程序源码modem卡驱动程序源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/*************************************************************************
*	File name		: zjmdmdrv.c
*	Subsystem	: modem卡驱动程序
*	Target env	: Linux
*	Author		: 谢红伟
*	Last modified	: 2002/04/30
*	Description	: Character device driver for zj-modem
*	Copyright	: zjkj
*	Note			: 
**************************************************************************/

#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif
/* for kernel and module */
#include <linux/kernel.h>
#include <linux/module.h>
/* for character device drive */
#include <linux/fs.h>	/*for O_NONBLOCK*/
#include <linux/wrapper.h>
/* for other function and macro */
#include <linux/errno.h>  
#include <asm/io.h>
#include <linux/ioport.h>
#if LINUX_VERSION_CODE == KERNEL_VERSION(2,4,7)
#include <linux/slab.h>
#endif
#if LINUX_VERSION_CODE == KERNEL_VERSION(2,4,2)
#include <linux/malloc.h>	/*for kmollac()*/
#endif
#include <linux/delay.h>	/*for udelay()*/
#include <linux/types.h>
#include <linux/time.h>	/*for do_gettimeofday()*/
#include <linux/param.h>	/*for HZ*/
#include <linux/interrupt.h>	/*for DECLARE_TASKLET()*/
#include <linux/wait.h>
#include <linux/mc146818rtc.h>	/* for get cmos time */
#include <linux/sched.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
#include <asm/uaccess.h>	/*for put_user() */
#endif
/* myself */
#include "zjmdmdrv.h"

/*************************************************************************
* 带参数的宏
**************************************************************************/
#define START_TIMER(p_ch) 															\
{ \
	(p_ch)->wtimer_started = TRUE; 													\
	(p_ch)->wtimer_start_time = jiffies_to_msec(jiffies); 									\
	(p_ch)->wbytes_after_pause = 0; 													\
}
#define  STOP_TIMER(p_ch) 															\
	(p_ch)->wtimer_started = FALSE

#define TIMER_ARRIVED(p_ch) 														\
	jiffies_to_msec(jiffies) - (p_ch)->wtimer_start_time >= (p_ch)->pause_time)

#define MDMNO_IS_NO_VALID(mdm_no) 												\
	(((u8)mdm_no)>=MDM_NUM)
	
#define CHLNO_IS_NO_VALID(chl_no) 													\
	(((u8)chl_no)>=CHL_NUM)

DECLARE_TASKLET(write_tasklet,TastletRoutine,0x0000);

/*************************************************************************
desc: 将一个十进制的表示数字的字符串str转成BCD码格式
,结果存入buf中。
ret : returns the length of the BCD numeric string.
**************************************************************************/
int  ConvertStrToBCD(char *str, char *buf)
{
	int i, j;
	unsigned char ch_tmp;
	for (i=0, j=0; str[i]!='\0'; i+=2)
	{
		if ((str[i] < '0') || (str[i] > '9')) break;
		ch_tmp = (str[i] - '0') << 4;
		if ((str[i+1] < '0') || (str[i+1] > '9'))
		{
			ch_tmp |= 0xf;
			buf[j++] = ch_tmp;
			break;
		}
		ch_tmp |= (str[i+1] - '0');
		buf[j++] = ch_tmp;
	}
	return  j;
}
//================================================//
//================================================//
//					处理时间的函数						//
//================================================//
//================================================//
#define EPOCH_YEAR 1970
#ifndef __isleap
#define	__isleap(year)	\
  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#endif
#define SECS_PER_HOUR	(60 * 60)
#define SECS_PER_DAY	(SECS_PER_HOUR * 24)
#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
#define TZ_MINUTES_WEST	(-240)	//时差校正时间(分)

/* How many days come before each month (0-12).  */
const unsigned short int __mon_yday[2][13] =
{
	/* Normal years.  */
	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
	/* Leap years.  */
	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};

/****************************************************************
将秒转成常用日期格式
*****************************************************************/
int ConvertTimeToStrInKernel(time_t tv_sec, long tv_usec,char *dest)
{
	long int days, rem, y;
	const unsigned short int *ip;
	unsigned int res_year,res_month,res_day,res_hour,res_minute,res_second,
//		res_centiseconds,res_hundredsOfMicroseconds,res_microseconds,
		res_milliseconds;
	tv_sec += TZ_MINUTES_WEST * 60;
	days = tv_sec / SECS_PER_DAY;
	rem = tv_sec % SECS_PER_DAY;
	res_hour = rem / SECS_PER_HOUR;
	rem %= SECS_PER_HOUR;
	res_minute = rem / 60;
	res_second = rem % 60;
	y = 1970;
	while (days < 0 || days >= (__isleap(y) ? 366 : 365))
	{
		long int yg = y + days / 365 - (days % 365 < 0);

		/* Adjust DAYS and Y to match the guessed year.  */
		days -= ((yg - y) * 365
			+ LEAPS_THRU_END_OF (yg - 1)
			- LEAPS_THRU_END_OF (y - 1));
		y = yg;
	}
	res_year = y;
	ip = __mon_yday[__isleap(y)];
	for (y = 11; days < (long int) ip[y]; --y)
		continue;
	days -= ip[y];
	res_month = y + 1;
	res_day = days + 1;
	/*
	res_centiseconds = tv_usec / 10000;	//厘秒,即百分之一秒
	res_hundredsOfMicroseconds = (tv_usec - res_centiseconds * 10000) / 100;//数百个微秒
	res_microseconds = (tv_usec - res_centiseconds * 10000 -
		res_hundredsOfMicroseconds * 100);	//微秒
	return sprintf(dest,"%.2d-%.2d %.2d:%.2d:%.2d:%.2d:%.2d:%.2d",
		res_month,res_day,res_hour,res_minute,res_second,
		res_centiseconds,res_hundredsOfMicroseconds,res_microseconds);
	*/
	res_milliseconds = tv_usec/1000;	//毫秒
	return sprintf(dest,"%.2d-%.2d %.2d:%.2d:%.2d:%.3d",
		res_month,res_day,res_hour,res_minute,res_second,res_milliseconds);
}

/**************************************************************
*将系统的jiffies滴答数转成毫秒数
***************************************************************/
inline u64 jiffies_to_msec(unsigned long jiffies)
{
	return jiffies*(1000/HZ);
}

/*********************************************************
*取得CMOS时间并转成字符串,返回秒数
*********************************************************/
u64 get_cmos_datetime(char *dest)
{
	unsigned int year, mon, day, hour, min, sec;
	int i;

	/* The Linux interpretation of the CMOS clock register contents:
	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
	 * RTC registers show the second which has precisely just started.
	 * Let's hope other operating systems interpret the RTC the same way.
	 */
	/* read RTC exactly on falling edge of update flag */
	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
			break;
	for (i = 0 ; i < 1000000 ; i++)	/* must try at least 2.228 ms */
		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
			break;
	do { /* Isn't this overkill ? UIP above should guarantee consistency */
		sec = CMOS_READ(RTC_SECONDS);
		min = CMOS_READ(RTC_MINUTES);
		hour = CMOS_READ(RTC_HOURS);
		day = CMOS_READ(RTC_DAY_OF_MONTH);
		mon = CMOS_READ(RTC_MONTH);
		year = CMOS_READ(RTC_YEAR);
	} while (sec != CMOS_READ(RTC_SECONDS));
	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
	  {
	    BCD_TO_BIN(sec);
	    BCD_TO_BIN(min);
	    BCD_TO_BIN(hour);
	    BCD_TO_BIN(day);
	    BCD_TO_BIN(mon);
	    BCD_TO_BIN(year);
	  }
	if ((year += 1900) < 1970)
		year += 100;
	if(dest)
		sprintf(dest,"%.4d-%.2d-%.2d %.2d:%.2d:%.2d",year, mon, day, hour, min, sec);
	return mktime(year, mon, day, hour, min, sec)-TZ_MINUTES_WEST*60;
}

/*********************************************************************
* 输出日志信息
*********************************************************************/
int  Log( u8 mdm_no,u8 chl_no, char *how, ...)
{
	va_list  va;
	char  buf[512];
	int i = 0;
	struct timeval tv;
	do_gettimeofday(&tv);
	i += sprintf(&buf[i], "[");
	i += ConvertTimeToStrInKernel(tv.tv_sec, tv.tv_usec,&buf[i]);
	if(mdm_no>7 || chl_no>3)
		i += sprintf(&buf[i], "%s xhw] : ",&buf[i]);
	else
		i += sprintf(&buf[i], "%s %d-%d] : ",&buf[i],mdm_no,chl_no);
	va_start (va, how);
	i += vsprintf(&buf[i], how,va);
	va_end(va);
	buf[i] = '\0';
	printk("%s",buf);
	//CorrectLinuxTimeByCMOS();
	return i;
}


/*********************************************************************
* 纠正系统时间
*********************************************************************/
void  CorrectLinuxTimeByCMOS()
{
	unsigned long cmos_sec,linux_sec;
	struct timeval tv;
	char cmos_timestr[32];
	cmos_sec = get_cmos_datetime(cmos_timestr);
	do_gettimeofday(&tv);
	linux_sec = tv.tv_sec;
	if(cmos_sec != linux_sec)
	{
		tv.tv_sec = cmos_sec;
#ifdef _DEBUG
		Log(INVALID_MDMNO,INVALID_CHLNO,
			"CMOS time : %s\n",cmos_timestr);
#endif
		do_settimeofday(&tv);
	}
	return;
}

/*****************************************************
type:private
* 检查读缓冲指针,并做相应的调整
****************************************************/
 char CheckReadBuffPointer(struct ChannelDesc *p_ch)
{
	if (p_ch->rbuf_readpos >= MAX_BUFF_SIZE)
	{
		p_ch->rbuf_readpos = 0;
	}
	if (p_ch->rbuf_writepos >= MAX_BUFF_SIZE)
	{
		p_ch->rbuf_writepos = 0;
	}
	/* 检查读缓冲是否已经满或空*/
	if (p_ch->rbuf_readpos == p_ch->rbuf_writepos)
	{
		return (p_ch->readable_len>0)? FULL:EMPTY;
	}
	else if(p_ch->readable_len<=0)
	{
		return EMPTY;
	}
	else
	{
		return NORMAL;
	}
}

/******************************************************
type:private
* 检查写缓冲指针位置,并做适当调整
******************************************************/
char CheckWriteBufferPointer(struct ChannelDesc *p_ch)
{
	if (p_ch->wbuf_readpos >= sizeof(p_ch->write_buf))
	{
		p_ch->wbuf_readpos = 0;
	}
	if (p_ch->wbuf_writepos >= sizeof(p_ch->write_buf))
	{
		p_ch->wbuf_writepos = 0;
	}
	/* 检查读缓冲是否已经满或空*/
	if (p_ch->wbuf_readpos == p_ch->wbuf_writepos)
	{
		return (p_ch->writeable_len>0)? FULL:EMPTY;
	}
	else if(p_ch->writeable_len<=0)
	{
		return EMPTY;
	}
	else
	{
		return NORMAL;
	}
}

/******************************************************
type:private
* 检查命令缓冲指针位置,并做适当调整
******************************************************/
char CheckCmdBufferPointer(struct MdmDesc *p_mdm)
{
	if (p_mdm->cbuf_readpos >= sizeof(p_mdm->cmd_buf))
	{
		p_mdm->cbuf_readpos = 0;
	}
	if (p_mdm->cbuf_writepos >= sizeof(p_mdm->cmd_buf))
	{
		p_mdm->cbuf_writepos = 0;
	}
	/* 检查读缓冲是否已经满或空*/
	if (p_mdm->cbuf_readpos == p_mdm->cbuf_writepos)
	{
		return (p_mdm->cmdable_len>0)? FULL:EMPTY;
	}
	else if(p_mdm->cmdable_len<=0)
	{
		return EMPTY;
	}
	else
	{
		return NORMAL;
	}
}

/***************************************************
* 等待队列进入超时睡眠
***************************************************/
ERRNO sleep_a_while(wait_queue_head_t *wait_queue,u32 a)
{
#ifdef _VERBOSE
	Log(INVALID_MDMNO,INVALID_CHLNO,"*** sleep: %d milli-seconds ***\n",a);
#endif
	interruptible_sleep_on_timeout(wait_queue, a*HZ/1000);
#ifdef _VERBOSE
	Log(INVALID_MDMNO,INVALID_CHLNO,"*** Awoken because timeout arrived ***\n",a);
#endif
	if (signal_pending(current)) 
	{
#ifdef _DEBUG
		Log(INVALID_MDMNO,INVALID_CHLNO,"Got signal when sleep on sleep_a_while()\n");
#endif
		return -ERESTARTSYS;	//tell the fs layer to handle it
	}
	return NO_ERROR;
}
/**********************************************
* type:private
* 检测一块modem卡是否可用
**********************************************/
BOOL DetectModem(u8 mdm_no)
{
	u16 max_times;
	BOOL mdm_is_ok;
	u8 data1,data2;
	mdm_is_ok = TRUE;
	//清端口数据
	inb(DataPort[mdm_no]);
	inb(StatePort[mdm_no]);
	//发检测卡的命令数据
	data1 = 0x08;
	data2 = CMD_DETECT;
	max_times = 0;
	while(!(inb(StatePort[mdm_no])&0x02))
	{
		if(max_times>=MAX_DETECT_TIMES)
		{
			mdm_is_ok = FALSE;
			break;
		}
		max_times++;
		udelay(1000);
	}
	if(!mdm_is_ok)
	{
#ifdef _VERBOSE
		Log(INVALID_MDMNO,INVALID_CHLNO,"Detect modem card NO.%d failed!\n",mdm_no);
#endif
		return FALSE;
	}
	mdm_is_ok = TRUE;
	outb(data1,DataPort[mdm_no]);
	max_times = 0;
	while(!(inb(StatePort[mdm_no])&0x02))
	{
		if(max_times>=MAX_DETECT_TIMES)
		{
			mdm_is_ok = FALSE;
			break;
		}
		max_times++;
		udelay(1000);
	}
	if(!mdm_is_ok)
	{
#ifdef _VERBOSE
		Log(INVALID_MDMNO,INVALID_CHLNO,"Detect modem card NO.%d failed!\n",mdm_no);
#endif
		return FALSE;
	}
	mdm_is_ok = TRUE;
	outb(data2,DataPort[mdm_no]);
	data1 = data2 = 0xff;
	mdm_is_ok = TRUE;
	max_times = 0;
	/* 读检测结果 */
	while(inb(StatePort[mdm_no])&0x01)		//无数据可读
	{
		if(max_times>=MAX_DETECT_TIMES)
		{
			mdm_is_ok = FALSE;
			break;
		}
		max_times++;
		udelay(1000);
	}
	data1 = inb(DataPort[mdm_no]);
	if(!mdm_is_ok)
	{
		if(data1== 0xff)
		{
#ifdef _DEBUG
			Log(INVALID_MDMNO,INVALID_CHLNO,"Modem card NO.%d is not exist!\n",mdm_no);
#endif
		}
		else
		{
			Log(INVALID_MDMNO,INVALID_CHLNO,"Modem card NO.%d is bad!\n",mdm_no);
		}
		return FALSE;
	}
	mdm_is_ok = TRUE;
	max_times = 0;
	while(inb(StatePort[mdm_no])&0x01)
	{
		if(max_times>=MAX_DETECT_TIMES)
		{
			mdm_is_ok = FALSE;
			break;
		}
		max_times++;
		udelay(1000);
	}
	data2 = inb(DataPort[mdm_no]);
	if(!mdm_is_ok)
	{
		if(data1== 0xff)
		{
#ifdef _DEBUG
			Log(INVALID_MDMNO,INVALID_CHLNO,"Modem card NO.%d is not exist!\n",mdm_no);
#endif
		}
		else
		{
			Log(INVALID_MDMNO,INVALID_CHLNO,"Modem card NO.%d is bad!\n",mdm_no);
		}
		return FALSE;
	}
	if((data1==0x0c) && (data2==CMD_FEEDBACK))
	{
		//清端口数据
		inb(DataPort[mdm_no]);
		inb(StatePort[mdm_no]);
		Log(INVALID_MDMNO,INVALID_CHLNO,"Detect modem card NO.%d is OK!\n",mdm_no);
		return TRUE;
	}
	return TRUE;
}

/**********************************************
*清空读缓冲
**********************************************/
inline void CleanReadBuff(struct ChannelDesc *p_ch)
{
	memset(p_ch->read_buf,0,sizeof(p_ch->read_buf));
	p_ch->rbuf_readpos = 0;
	p_ch->rbuf_writepos = 0;
	p_ch->readable_len = 0;
	p_ch->rbuf_max_full_times = 0;
}

/**********************************************
*清写读缓冲
**********************************************/
inline void CleanWriteBuff(struct ChannelDesc *p_ch)
{
	memset(p_ch->write_buf,0,sizeof(p_ch->write_buf));
	p_ch->wbuf_readpos = 0;
	p_ch->wbuf_writepos = 0;
	p_ch->writeable_len = 0;
	p_ch->wbytes_once = MAX_BUFF_SIZE;
	p_ch->pause_time = 0;
	p_ch->wbytes_after_pause = 0;
	p_ch->wtimer_started = FALSE;
	memset((char*)&p_ch->wtimer_start_time,0,sizeof(p_ch->wtimer_start_time));
	p_ch->wbuf_max_full_times = 0;
}

/*********************************************
type:pvivate
* 初始化modem卡通道的相关资源
*********************************************/
void InitChannelVariable(struct ChannelDesc *p_ch)
{
	p_ch->connect = FALSE;
	p_ch->connect_state = INVALID_STATE;
	p_ch->hangup_state = INVALID_STATE;
	CleanReadBuff(p_ch);
	CleanWriteBuff(p_ch);	
	return;
}

/*********************************************
type:pvivate
* 初始化modem卡模块的相关资源

⌨️ 快捷键说明

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