PPP has a bunch of sub-protocols that you’ll need to deal with in order to get an actual IP connection. What’s going on is that the phone is emulating a Hayes modem and a dialup ISP. You need to get past all that in order to get an IP packet to the GPRS network.
I guess you’ll need rfc1661 for the basic outline of PPP, rfc1332 for IPCP (to tell PPP you’re making an IP connection instead of, say, an X.25 connection), and possibly rfc1334 for PAP (sending a password — completely pointless, but your device might require it anyway). That’s a lot of RFCs, but the nice thing is that since they’re internet standards instead of ISO standards, they’re all freely available.
This is all unnecessarily baroque and complex, but it seems to be the only way to do it. I really wish there were a way to get to the GPRS interface more directly instead of going through all this emulated rigamarole, but I haven’t seen an implementation that does.
Of course, once you get the IP connection set up, you still need to deal with the usual IP issues — hostname resolution (DNS), and TCP, and whatever protocol you’re running on top of TCP (SMTP, HTTP, etc.). If you can, I suggest not trying to write all this yourself.
Here’s an example of my laptop setting up a connection to my cell phone, just to give an example of the PPP setup conversation:
23:40:24: Write : AT\13
23:40:24: Matched : OK\13\10
23:40:24: Write : AT+CGMI\13
23:40:24: Matched : Nokia
23:40:24: Write : AT&FE0V1&D2&S0&C1S0=0+IFC=0,2\13
23:40:24: Matched : OK\13\10
23:40:26: Write : AT+CBST=14,0,1\13
23:40:26: Matched : OK\13\10
23:40:26: Dialling *99#
23:40:26: Write : ATX5DT*99#\13
23:40:26: Matched : CONNECT
23:40:30: Serial connection established.
23:40:31: Connect: ppp0 <--> /dev/cu.Bluetooth-Modem
23:40:32: sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x8be97632> <pcomp> <accomp>]
23:40:32: rcvd [LCP ConfRej id=0x1 <magic 0x8be97632> <pcomp> <accomp>]
23:40:32: sent [LCP ConfReq id=0x2 <asyncmap 0x0>]
23:40:32: rcvd [LCP ConfAck id=0x2 <asyncmap 0x0>]
23:40:32: rcvd [LCP ConfReq id=0x0 <auth pap> <mru 1500> <asyncmap 0xa0000>]
23:40:32: No auth is possible
23:40:32: lcp_reqci: returning CONFREJ.
23:40:32: sent [LCP ConfRej id=0x0 <auth pap>]
23:40:32: rcvd [LCP ConfReq id=0x1 <mru 1500> <asyncmap 0xa0000>]
23:40:32: lcp_reqci: returning CONFACK.
23:40:32: sent [LCP ConfAck id=0x1 <mru 1500> <asyncmap 0xa0000>]
23:40:32: sent [IPCP ConfReq id=0x1 <compress VJ 0f 01> <addr 0.0.0.0> <ms-dns1 0.0.0.0> <ms-dns3 0.0.0.0>]
23:40:32: rcvd [IPCP ConfReq id=0x0 <addr 10.6.6.6>]
23:40:32: ipcp: returning Configure-ACK
23:40:32: sent [IPCP ConfAck id=0x0 <addr 10.6.6.6>]
23:40:33: rcvd [IPCP ConfRej id=0x1 <compress VJ 0f 01>]
23:40:33: sent [IPCP ConfReq id=0x2 <addr 0.0.0.0> <ms-dns1 0.0.0.0> <ms-dns3 0.0.0.0>]
23:40:33: rcvd [IPCP ConfNak id=0x2 <addr 10.169.61.47> <ms-dns1 66.94.9.120> <ms-dns3 66.94.25.120>]
23:40:33: sent [IPCP ConfReq id=0x3 <addr 10.169.61.47> <ms-dns1 66.94.9.120> <ms-dns3 66.94.25.120>]
23:40:33: rcvd [IPCP ConfAck id=0x3 <addr 10.169.61.47> <ms-dns1 66.94.9.120> <ms-dns3 66.94.25.120>]
You can see it dialling the (fake) modem, then doing PPP negotiations (each line corresponds to one PPP packet). At the end of this, the host knows what IP address it’s been assigned and knows what DNS servers to use, and can start sending IP packets across the link.