The "echo" app is still not playing back (on BT200) but it has improved.
[firmerware] / src / net / llconly.c
1 /* llconly.c -- Network functions covering LLC only.
2  *
3  * This file is part of 0cpm Firmerware.
4  *
5  * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
6  *
7  * 0cpm Firmerware is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation, version 3.
10  *
11  * 0cpm Firmerware is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 /**
22  * This mini network stack is designed for very simple targets,
23  * such as network testing or a bootloader.  It only supports
24  * IEEE 802.2, that is, LLC.  And it even does a fairly modest
25  * job at that.
26  *
27  * This code is suitable for running a network console over LLC2
28  * and for running TFTP over LLC1.  It was not designed with any
29  * higher purpose in mind.  It is not designed to integrate with
30  * a more complete network stack like the phone application's.
31  * Hence the name llconly.c!
32  *
33  * Use this minimal network stack where you have no other way of
34  * extracting logs, or exchanging bootloadables.  There is an
35  * alternative LLC implementation in the "real" phone firmware,
36  * which is much better because it can rely on resend timers.
37  *
38  * From: Rick van Rein <rick@openfortress.nl>
39  */
40
41
42 #include <stdlib.h>
43 #include <stdint.h>
44 #include <stdbool.h>
45 #include <stdarg.h>
46 #include <string.h>
47
48 #include <config.h>
49
50 #include <0cpm/cons.h>
51 #include <0cpm/netinet.h>
52 #include <0cpm/netfun.h>
53
54
55
56 /* Construct an LLC reply start; fill out ethernet addresses and SSAP/DSAP */
57 uint8_t *netreply_llc (uint8_t *pout, intptr_t *mem) {
58         memcpy (pout +  0, ((uint8_t *) mem [MEM_ETHER_HEAD] + 6), 6);
59         bottom_flash_get_mac (pout + 6);
60         pout [14] = mem [MEM_LLC_SSAP];
61         pout [15] = mem [MEM_LLC_DSAP];
62 }
63
64
65 static bool llc_connected = false;
66 static uint8_t peer_sap;
67 static uint8_t peer_mac [6];
68 static uint8_t llc_ua [6 + 6 + 2 + 3];
69 static uint8_t llc_rr [6 + 6 + 2 + 4];
70 static uint8_t llc_sent;
71 static uint8_t llc_received;
72 static uint8_t llc_input;
73 static intptr_t mem [MEM_NETVAR_COUNT];
74
75
76 struct llc2 {
77         uint8_t dummy;
78 };
79 static struct llc2 llc2_dummy_handle;
80
81
82 /* Dummy LLC2 send routine, ignoring "cnx" as there is just one LLC2 connection.
83  * Before sending, the routine will first establish whether the last send
84  * was successful; if not, it will repeat that.  The return value is true if at
85  * least the new send was done, relying on future calls to resend if need be.
86  */
87 uint8_t llc_pkt [100];
88 uint16_t llc_pktlen;
89 bool netsend_llc2 (struct llc2 *cnx, uint8_t *data, uint16_t datalen) {
90         bool newpkt;
91         if (datalen > 80) {
92                 return false;
93         }
94         if (!llc_connected) {
95                 return false;
96         }
97         newpkt = (llc_sent == llc_received);
98         if (newpkt) {
99                 // Sending is complete, construct new packet as requested
100                 memcpy (llc_pkt +  0, peer_mac, 6);
101                 memcpy (llc_pkt +  6, "\x00\x0b\x82\x19\xa0\xf4", 6);
102                 llc_pkt [12] = 0x00;
103                 llc_pkt [13] = datalen + 4;
104                 llc_pkt [14] = peer_sap;                // DSAP
105                 llc_pkt [15] = 20;                      // SSAP
106                 llc_pkt [16] = llc_sent << 1;           // N(S) = 0x00, information frame
107                 llc_pkt [17] = llc_input << 1;          // N(R) = sent-up-to-here, low bit reset
108                 memcpy (llc_pkt + 18, data, datalen);
109                 llc_pktlen = 6 + 6 + 2 + 4 + datalen;
110                 llc_sent++;
111                 llc_sent &= 0x7f;
112         }
113         bottom_network_send (llc_pkt, llc_pktlen);
114         return newpkt;
115 }
116
117 /* LLC-only network packet handling, specifically for:
118  *  - LLC2 console at SAP 20
119  *  - LLC1 firmware access through TFTP at SAP 68
120  */
121 void nethandler_llconly (uint8_t *pkt, uint16_t pktlen) {
122         uint16_t typelen;
123         uint16_t cmd;
124         bool ack = false;
125         bool type1;
126         typelen = (pkt [12] << 8) | pkt [13];
127 #if 0
128         if ((pktlen < 14) || (typelen < 46)) {
129 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
130                 bottom_printf ("Unpadded packet length %d received\n", (intptr_t) pktlen);
131 #endif
132                 return;
133         }
134 #endif
135         if (typelen > 1500) {
136 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
137                 bottom_printf ("Traffic is not LLC but protocol 0x%4x\n", (intptr_t) typelen);
138 #endif
139                 return;
140         }
141         if ((typelen > 64) && (typelen != pktlen - 14)) {
142 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
143                 bottom_printf ("Illegal length %d received (pktlen = %d)\n", (intptr_t) typelen, (intptr_t) pktlen);
144 #endif
145                 return;
146         }
147 #if 0
148         if (pkt [14] != 20) {
149 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
150                 bottom_printf ("Received LLC traffic for SAP %d instead of 20\n", pkt [14]);
151 #endif
152                 return;
153         }
154 #endif
155         cmd = pkt [16];
156         type1 = (cmd & 0x03) == 0x03;
157         if (!type1) {
158                 cmd |= pkt [17] << 8;
159         }
160         if (llc_connected && !type1) {
161                 if (memcmp (pkt + 6, peer_mac, 6) != 0) {
162                         // Peer MAC should be the constant while connected
163                         return;
164                 }
165                 if ((pkt [15] & 0xfe) != peer_sap) {
166                         // Peer SAP should be constant while connected
167                         return;
168                 }
169         }
170         if (cmd == 0x03) {                              // UI (llc.datagram)
171 #ifdef CONFIG_FUNCTION_FIRMWARE_UPGRADES
172                 if (pkt [14] == 68) {
173                         uint16_t pktlen;
174                         // Setup minimal mem[] array for TFTP over LLC1
175                         bzero (mem, sizeof (mem));
176                         mem [MEM_ETHER_HEAD] = (intptr_t) pkt;
177                         mem [MEM_ALL_DONE] = (intptr_t) &pkt [pktlen];
178                         mem [MEM_LLC_DSAP] = pkt [14];
179                         mem [MEM_LLC_SSAP] = pkt [15];
180                         pkt = netllc_tftp (pkt, mem);
181                         pktlen = mem [MEM_ALL_DONE] - (intptr_t) pkt;
182                         // Send and forget -- LLC1 is unconfirmed transmission
183                         bottom_network_send (pkt, pktlen);
184                 } else {
185 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
186                         bottom_printf ("LLC1 UA is only used for TFTP, use SAP 68 and not %d\n", (intptr_t) pkt [14]);
187 #endif
188                 }
189 #else
190 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
191                 bottom_printf ("No bootloader -- ignoring TFTP over LLC1\n");
192 #endif
193         } else if (pkt [14] != 20) {
194 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
195                 bottom_printf ("To access the network console, use SAP 20 and not %d\n", (intptr_t) pkt [14]);
196 #endif
197 #endif
198         } else if (cmd == 0x7f) {                       // SABME (llc.connect)
199                 memcpy (peer_mac, pkt + 6, 6);
200                 peer_sap = pkt [15] & 0xfe;
201                 llc_sent = llc_received = llc_input = 0x00;
202                 llc_connected = true;
203                 netcons_connect (&llc2_dummy_handle);
204                 ack = true;
205         } else if (cmd == 0x53) {                       // DISC  (llc.disconnect)
206                 llc_connected = false;
207                 netcons_close ();
208                 ack = true;
209         } else if (cmd == 0x87) {                       // FRMR (llc.framereject)
210                 llc_received = llc_sent;
211         } else if ((cmd & 0x0001) == 0x0000) {          // Data sent back (will be ignored)
212                 memcpy (llc_rr +  0, peer_mac, 6);
213                 memcpy (llc_rr +  6, "\x00\x0b\x82\x19\xa0\xf4", 6);
214                 memcpy (llc_rr + 12, "\x00\x04\x00\x15", 4);
215                 llc_rr [14] = peer_sap;
216                 llc_rr [16] = 0x01;                     // supervisory, RR
217                 llc_rr [17] = (cmd + 2) & 0xfe;         // outgoing N(R) = incoming N(S) + 1
218                 bottom_network_send (llc_rr, sizeof (llc_rr));
219         } else if ((cmd & 0x0007) == 0x0001) {          // Receiver ready / Receiver Reject
220                 llc_received = (cmd >> 9);
221         } else {
222 #ifdef CONFIG_MAINFUNCTION_DEVEL_NETWORK
223                 bottom_printf ("Selfishly ignoring LLC traffic with cmd bytes 0x%2x%2x\n",
224                                         (intptr_t) pkt [16], (intptr_t) pkt [17]);
225 #endif
226         }
227         if (ack) {
228                 memcpy (llc_ua +  0, peer_mac, 6);
229                 memcpy (llc_ua +  6, "\x00\x0b\x82\x19\xa0\xf4", 6);
230                 memcpy (llc_ua + 12, "\x00\x03\x00\x15\x73", 5);
231                 llc_ua [14] = peer_sap;
232                 // Try sending; assume repeat will be caused by other/smarter side
233                 bottom_network_send (llc_ua, sizeof (llc_ua));
234         }
235 }
236