📄 zjmdmdrv.c
字号:
/*************************************************************************
* 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 + -