第一步:编写GPRS的内核驱动程序
因为我用的开发板基于ARM920T,linux内核中没有mc35i的驱动程序,所以自己写了个驱动程序,重新编译了内核。驱动程序代码如下:/**//* * 作者:龙涛 */ #define __NO_VERSION__ #include <linux/module.h> #include <linux/version.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/errno.h> #include <linux/timer.h> #include <linux/delay.h> #include <linux/config.h> #include <linux/mm.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/ioport.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/hardware.h> #include <asm/uaccess.h> #undef DEBUG_Mc35i //#define DEBUG_Mc35i 1 #ifdef DEBUG_Mc35i #define DBG_Mc35i(fmt, args...) printk(fmt,## args) #else #define DBG_Mc35i(fmt, args...) #endif static int Mc35i_open(struct inode *, struct file *); static int Mc35i_close(struct inode *, struct file *); static int Mc35i_read(struct file *, int *, size_t, loff_t *); static int Mc35i_write(struct file *, const char *, size_t, loff_t *); //static void Mc35i_interrupt(int, void *, struct pt_regs *); static void Mc35i_hardware_init(void); #define Mc35i_MAJOR 253 #define TRAN_CMD 1 #define TRAN_CMD_DATA 2 #define READ_CMD_DATA 3 /**//* User CMD */ #define CMD_IGT '0' #define CMD_EMERGOFF '1' /**//* PC14, PC15, PC5 for Mc35i */ #define MC35_IGT AT91C_PIO_PC14 #define MC35_EMERGOFF AT91C_PIO_PC15 #define MC35_ST AT91C_PIO_PC5 static struct file_operations Mc35i_fops = ...{ open: Mc35i_open, read: Mc35i_read, write: Mc35i_write, release: Mc35i_close, }; static int input_flag = 0; static unsigned char input_data = 0xff; static void Mc35i_hardware_init (void) ...{ DBG_Mc35i("Mc35i_hardware_init "); AT91_SYS->PIOC_PER |= MC35_IGT | MC35_EMERGOFF|MC35_ST; AT91_SYS->PIOC_OER |= MC35_IGT | MC35_EMERGOFF; AT91_SYS->PIOC_ODR |= MC35_ST; AT91_SYS->PIOC_CODR |= MC35_IGT| MC35_EMERGOFF; //ignition when system init } static int Mc35i_open (struct inode *inode, struct file *file) ...{ DBG_Mc35i("Mc35i_open "); MOD_INC_USE_COUNT; return 0; } static int Mc35i_close (struct inode *inode, struct file *file) ...{ DBG_Mc35i("Mc35i_close "); MOD_DEC_USE_COUNT; return 0; } static int Mc35i_read (struct file *file, int *buf, size_t count, loff_t *ppos) ...{ unsigned int data; DBG_Mc35i("Mc35i_read: input_flag %d ", input_flag); data = AT91_SYS->PIOC_PDSR & MC35_ST; printk("status of PC5 is %d ", data); put_user(data, buf); return count; } static int Mc35i_write (struct file *file, const char *buf, size_t count, loff_t *ppos) ...{ unsigned char cmd_type; DBG_Mc35i("Mc35i_write: %p ", buf); get_user(cmd_type, buf); switch (cmd_type) ...{ case CMD_IGT: AT91_SYS->PIOC_SODR |= MC35_IGT; //enable /IGT mdelay(10); AT91_SYS->PIOC_CODR |= MC35_IGT; //disable /IGT mdelay(100); AT91_SYS->PIOC_SODR |= MC35_IGT; //enable /IGT break; case CMD_EMERGOFF: AT91_SYS->PIOC_CODR |= MC35_EMERGOFF; break; default: DBG_Mc35i("Mc35i_write: cmd_type = %d error ", cmd_type); break; } return count; } static void __init Mc35i_init (void) ...{ //int ret; Mc35i_hardware_init(); if(register_chrdev(Mc35i_MAJOR, "Mc35i", &Mc35i_fops))...{ DBG_Mc35i("register_chrdev for Mc35i error "); goto fail_register_chrdev; } printk(KERN_INFO __FILE__ ": gprsMc35i for AT91RM9200 "); return; fail_register_chrdev: return; } static void __exit Mc35i_cleanup (void) ...{ unregister_chrdev(Mc35i_MAJOR, "Mc35i"); return; } module_init(Mc35i_init); module_exit(Mc35i_cleanup);编译内核后,要在文件系统中创建设备,命令如下:
mknod Mc35i c 253 0
第二步:移植ppp协议
由于arm-linux上没有提供使用ppp的pppd, chat应用程序,所以需要向开发板上移植。方法就和普通的应用程序一样,在pc机上的develop目录下面编译,然后将生成的可执行程序复制到Ramdisk文件系统中,在下次重新下载内核和文件系统时使用新的文件系统,就可以在arm-linux上运行pppd了。具体过程如下:1,在samba.org下载ppp-2.4.3.tar.gz后安如下操作:
tar zxvf ppp-2.4.3.tar.gz
2,由于在ppp根文件夹目录下面没有makefile文件,所以要自己添加:
# PPP top-level Makefile for Linux. DESTDIR = /usr/local BINDIR = $(DESTDIR)/sbin INCDIR = $(DESTDIR)/include MANDIR = $(DESTDIR)/share/man ETCDIR = /etc/ppp # uid 0 = root INSTALL= install all: cd chat; $(MAKE) $(MFLAGS) all cd pppd/plugins; $(MAKE) $(MFLAGS) all cd pppd; $(MAKE) $(MFLAGS) all cd pppstats; $(MAKE) $(MFLAGS) all cd pppdump; $(MAKE) $(MFLAGS) all install: $(BINDIR) $(MANDIR)/man8 install-progs install-devel install-progs: cd chat; $(MAKE) $(MFLAGS) install cd pppd/plugins; $(MAKE) $(MFLAGS) install cd pppd; $(MAKE) $(MFLAGS) install cd pppstats; $(MAKE) $(MFLAGS) install cd pppdump; $(MAKE) $(MFLAGS) install install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets $(ETCDIR)/chap-secrets install-devel: cd pppd; $(MAKE) $(MFLAGS) install-devel $(ETCDIR)/options: $(INSTALL) -c -m 644 etc.ppp/options $@ $(ETCDIR)/pap-secrets: $(INSTALL) -c -m 600 etc.ppp/pap-secrets $@ $(ETCDIR)/chap-secrets: $(INSTALL) -c -m 600 etc.ppp/chap-secrets $@ $(BINDIR): $(INSTALL) -d -m 755 $@ $(MANDIR)/man8: $(INSTALL) -d -m 755 $@ $(ETCDIR): $(INSTALL) -d -m 755 $@ clean: rm -f `find . -name '*.[oas]' -print` rm -f `find . -name 'core' -print` rm -f `find . -name '*~' -print` cd chat; $(MAKE) clean cd pppd/plugins; $(MAKE) clean cd pppd; $(MAKE) clean cd pppstats; $(MAKE) clean cd pppdump; $(MAKE) clean dist-clean: clean rm -f Makefile `find . -name Makefile -print` #kernel: # cd linux; ./kinstall.sh # no tests yet, one day... installcheck: true
至于其他目录下的makefile可以复制该目录下的makefile.linux即可。交叉编译后得到chat、pppd、pppstats、pppdump四个应用程序,拷贝到嵌入式系统的文件系统ramdisk/target/usr/sbin目录下,权限改成755即可。
然后在ramdisk/target/dev目录下建立ppp节点:
mknod ppp -c 108,0
运行PPPD看看, 若说segment fault, 则是内核没有配置mmap, 或者把刚才的Makefile中的 HAVE_MMAP改为FALSE, 因为pppd中调用了mmap().
PPP拨号脚本有ppp-on:
#!/bin/sh # # Script to initiate a ppp connection. This is the first part of the # pair of scripts. This is not a secure pair of scripts as the codes # are visible with the 'ps' command. However, it is simple. # # These are the parameters. Change as needed. TELEPHONE=*99***1# # The telephone number for the connection ACCOUNT= # The account name for logon (as in 'George Burns') PASSWORD= # The password for this account (and 'Gracie Allen') LOCAL_IP=0.0.0.0 # Local IP address if known. Dynamic = 0.0.0.0 REMOTE_IP=0.0.0.0 # Remote IP address if desired. Normally 0.0.0.0 NETMASK=255.255.255.0 # The proper netmask if needed # # Export them so that they will be available at 'ppp-on-dialer' time. export TELEPHONE ACCOUNT PASSWORD # # This is the location of the script which dials the phone and logs # in. Please use the absolute file name as the $PATH variable is not # used on the connect option. (To do so on a 'root' account would be # a security hole so don't ask.) # DIALER_SCRIPT=/mnt/ppp-on-dialer #这里改成自己文件所在的路径 # # Initiate the connection # # I put most of the common options on this command. Please, don't # forget the 'lock' option or some programs such as mgetty will not # work. The asyncmap and escape will permit the PPP link to work with # a telnet or rlogin connection. You are welcome to make any changes # as desired. Don't use the 'defaultroute' option if you currently # have a default route to an ethernet gateway. # exec /usr/sbin/pppd debug lock modem /dev/ttyS1 19200 asyncmap 20A0000 escape FF kdebug 0 $LOCAL_IP:$REMOTE_IP noipdefault netmask $NETMASK defaultroute connect $DIALER_SCRIPT
如果是Modem拨号,ppp-on为:
#!/bin/sh # # Script to initiate a ppp connection. This is the first part of the # pair of scripts. This is not a secure pair of scripts as the codes # are visible with the 'ps' command. However, it is simple. # # These are the parameters. Change as needed. TELEPHONE=016300 # The telephone number for the connection ACCOUNT=163 # The account name for logon (as in 'George Burns') PASSWORD=163 # The password for this account (and 'Gracie Allen') LOCAL_IP=0.0.0.0 # Local IP address if known. Dynamic = 0.0.0.0 REMOTE_IP=0.0.0.0 # Remote IP address if desired. Normally 0.0.0.0 NETMASK=255.255.255.0 # The proper netmask if needed # # Export them so that they will be available at 'ppp-on-dialer' time. export TELEPHONE ACCOUNT PASSWORD # # This is the location of the script which dials the phone and logs # in. Please use the absolute file name as the $PATH variable is not # used on the connect option. (To do so on a 'root' account would be # a security hole so don't ask.) # DIALER_SCRIPT=/mnt/ppp-on-dialer-modem # # Initiate the connection # # I put most of the common options on this command. Please, don't # forget the 'lock' option or some programs such as mgetty will not # work. The asyncmap and escape will permit the PPP link to work with # a telnet or rlogin connection. You are welcome to make any changes # as desired. Don't use the 'defaultroute' option if you currently # have a default route to an ethernet gateway. # exec /usr/sbin/pppd debug lock modem crtscts /dev/ttyS1 115200 asyncmap 20A0000 escape FF kdebug 0 $LOCAL_IP:$REMOTE_IP noipdefault netmask $NETMASK defaultroute connect $DIALER_SCRIPT
GPRS的ppp-on-dialer脚本如下:
#!/bin/sh # # This is part 2 of the ppp-on script. It will perform the connection # protocol for the desired connection. # exec chat -v TIMEOUT 3 ABORT ' BUSY ' ABORT ' NO ANSWER ' ABORT ' RINGING RINGING ' '' AT TIMEOUT 30 OK AT+CMEE=1 OK AT+CGDCONT=1,"ip","cmnet" OK AT+CGATT=1 OK ATDT*99***1# CONNECT
Modem的ppp-on-dialer如下:
#!/bin/sh # # This is part 2 of the ppp-on script. It will perform the connection # protocol for the desired connection. # exec chat -v TIMEOUT 3 ABORT ' BUSY ' ABORT ' NO ANSWER ' ABORT ' RINGING RINGING ' '' AT TIMEOUT 30 OK ATDT$TELEPHONE sername:--sername: $ACCOUNT assword: $PASSWORD
第三步:C程序的实现
//GPRS初始化 int InitGPRS(char *IP) { FILE *ppp0; int status; char buf[100]; int nIndex = 0; char *ch=NULL,*nlch=NULL; //判断是否存在IP status = system("ifconfig ppp0>/ppp0"); if(status == 127||status == -1||status != 0) { //打开设备 if(confData->WorkType == TYPE_GPRS) { if ((mc35i = open("/dev/Mc35i",O_RDWR|O_NOCTTY))<0) { printf("Warning:Open GPRS Mc35i failed! "); return -1; } else { printf("OK:Open GPRS is ok! "); } while(1) { nIndex++; if(nIndex > 30) return -1; if (!GPRSPowerIsOn()) { printf("Warning:gprs power is not on! "); write(mc35i,"0",1); //ignition mc35i sleep(2); } else break; } } //执行ppp拨号程序 if (!vfork()) { //GPRS方式 if(confData->WorkType == TYPE_GPRS) { printf("Exec GPRS ppp-on...... "); if(execl("./ppp-on","ppp-on",NULL)<0) //if (execvp("pppd",pppdScript)<0) { printf("Exec ppp-on error:%s! ",strerror(errno)); return -1; } } //MODEM方式 else { printf("Exec MODEM ppp-on...... "); if(execl("./ppp-on-modem","ppp-on",NULL)<0) { printf("Exec ppp-on error:%s! ",strerror(errno)); return -1; } } } sleep(40); //获取分配的IP地址 printf("Exec ifconfig ppp0 > ppp0...... "); status = system("ifconfig ppp0>/ppp0"); if(status == 127||status == -1||status != 0) { printf("Exec ifconfig ppp0 error! "); GPRSPoweroff(); return -1; } } printf("Open file ppp0...... "); if ((ppp0 = fopen("/ppp0","r")) <= 0) { printf("Open ppp0 info file fail! "); GPRSPoweroff(); return -1; } /*格式: # ifconfig ppp0 ppp0 Link encap:Point-to-Point Protocol inet addr:211.74.48.254 P-t-P:211.74.48.1 Mask:255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1492 Metric:1 RX packets:5 errors:0 dropped:0 overruns:0 frame:0 TX packets:3 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:3 */ if (fread(buf,sizeof(char),sizeof(buf),ppp0)>0) { printf("Get ppp0 IP...... "); ch = strchr(buf,' '); ch = strchr(ch,':'); nlch = strchr(ch,'P'); memcpy(IP,++ch,(nlch-ch)-2); }