Working KSZ8842 network driver; working LLC console routines (except va_list pass...
authorRick van Rein <vanrein@hwdev.(none)>
Thu, 5 May 2011 20:02:04 +0000 (20:02 +0000)
committerRick van Rein <vanrein@hwdev.(none)>
Thu, 5 May 2011 20:02:04 +0000 (20:02 +0000)
Beginnings of sound chip interaction (TLV320AIC2x)

19 files changed:
TODO
doc/porting.rst
doc/top2bottom.rst
include/0cpm/cons.h [new file with mode: 0644]
include/bottom/grandstream.h
include/bottom/ksz8842.h [new file with mode: 0644]
include/bottom/tic55x.h
src/driver/Makefile
src/driver/ksz8842.c [new file with mode: 0644]
src/driver/net/rtl8019as.c
src/driver/tic55x/grandstream-bt20x.c
src/driver/tic55x/isrmap.asm
src/driver/tlv320aic2x.c [new file with mode: 0644]
src/function/Kconfig
src/function/Kconfig.devel
src/function/Makefile
src/function/develtest/echo.c [new file with mode: 0644]
src/function/develtest/netconsole.c
src/function/netconsole.c [new file with mode: 0644]

diff --git a/TODO b/TODO
index ae699f6..aac104d 100644 (file)
--- a/TODO
+++ b/TODO
@@ -30,3 +30,8 @@ look at:
   they could be, there is no neighbour or ARP cache, there is no
   storage for servers for dns, ntp, sip-proxy.
 
+* tic55x's int.c driver: work out conditions for hibernation sleep.
+
+* tic55x's PLL: get better performance by increasing clock rate.  This
+  will influence other forms of timing, such as RAM, Network and Codec.
+
index 1ea0769..1548728 100644 (file)
@@ -100,11 +100,80 @@ on hand:
 * Linux PC (Windows might work as a sub-optimal substitute)
 * useful if available: oscilloscope, logic analyser
 
-The multimeter should be sensitive enough to measure through
+The **multimeter** should be sensitive enough to measure through
 a device without damaging it.  Its beep is very helpful because
 it allows you to drag one pin over the many pins of a large chip
 to find the connection to another point in the circuit.
 
+As an electronics engineer, I usually have a gut feeling how things
+are wired.  Using the multimeter, I could establish these things.
+I would switch off the device, set it in the ohmic beeping mode, place
+one probe of the multimeter on the pin I wanted to trace and then
+poke around.  Where I suspected connectivity to a chip, I would pull
+the second probe along the many pins of such a chip.  I would make a
+complete round before zooming in on a single beep -- sometimes you
+expect to find a signal but you are actually measuring GND, and that
+intuition is delivered audibly as several beeps on the same chip.  If
+you hit a single pin, always double check the pins around the suspect,
+as you will sometimes be probing two pins instead of just one.
+
+It's not much fun counting pins, but there are usually markings on
+the PCB's silk screen for every fifth pin.  Always do this work in
+broad daylight; nothing can beat the Sun for proper illumination.
+Closing one eye may help, and using a magnifying glass may help too.
+
+And yes... I would love to have probes with one conductive side and
+one isolated side, so I could poke it in between two pins instead
+of balancing it on top of one while moving something else around.
+Or, better even, it would be good to have a probe shaped like a
+cogwheel, that would rotate over the pins and show which one is the
+connected pin.  But those are just silly dreams.
+
+An **oscilloscope** is useful for testing analog signals; in a digital
+phone, these are mostly limited to clock signals and sound I/O; in
+an ATA there would be a lot more use for an oscilloscope.  Since
+the signals are not always repeating, a digital scope is the best
+option.  These often come with a logic analyser as an optional extra.
+
+A **logic analyser** can save a lot of work (an excellent reason to
+finally get one!) because it makes it possible to observe signals
+that are being sent to unknown or uncertain components.
+For reverse engineering the BT200, a logic analyser was useful for
+confirming that the LCD driver chip was indeed (acting like) a
+HT1621D, without having to program a driver and be left with the
+uncertainty if the guessed chip was wrong, or the frequencies.
+Also, it helped to read out the configuration codes sent to the
+LCD driver.  Later on, it showed that the LCD driver that I wrote
+did not behaving well on this bus, even if it was not visible;
+there were small glitches in the signal as a result of writing
+16-bit values to an 8-bit bus; the high part of the word was filled
+with zeroes and would make observed signals low for a very short
+time where they were supposed to remain high.  Finally,
+communication with the codec (sound I/O chip) can easily be viewed
+with a logic analyser.  In an ATA, a few more components apply.
+
+.. figure:: pix/ht162x.png
+       :scale: 200
+       :alt: Logic Analyser at work
+
+       The logic analyser at work.  Shown is the LCD driver of
+       the BT200 phone, for which we guessed it could be a
+       HT162x, based on searching the connector's pin markings
+       on the PCB.  D4 is the DATA signal, D3 is CS, D2 is
+       the RD clock (unconnected in the phone), D1 is the WR
+       clock.  At every rising edge of D1, the value of D4 is
+       checked into the LCD.  Note that D1 is always stable at
+       that time, which does not undermine this behaviour from
+       the assumed datasheet. The period of a command is marked
+       by D3.  12 bits makes it probable that this is a HT162x,
+       and the command confirms that: 100.1110.0011.0 is the
+       LCD command to set NORMAL mode (not TEST mode) and this
+       makes sense, also in the combination with other commands
+       observed.  This confirmed the HT162x family's behaviour.
+       Based on the empty holes that once held a crystal and
+       the number of segments on the display this was further
+       refined, and the LCD driver had to be a HT1621D chip.
+
 
 Understanding your hardware
 ===========================
@@ -157,7 +226,7 @@ The elements usually found in a phone are:
 * A codec chip.  These are a bit like embedded sound cards; they can
   be accessed over a protocol like SPI to interact with microphones,
   speakers and so on.  They usually include analog electronics such
-  as amplifiers.
+  as amplifiers and anti-aliasing filters.
 
 Don't forget to check the PCB's bottom; there may be components on
 both sides!
@@ -416,13 +485,14 @@ Build 3: Keys and display
 In ``make menuconfig``, select the firmware function ``Test keyboard / display``
 that will scan the keyboard and write its findings to the display.
 
-TODO: To build this, you would normally have to write a timer setup and
-interrupt service routine to handle ``bottom_time()`` and
-``bottom_set_timer_set()`` --do not forget to return the old setting for
-the latter-- in addition to the previously written ``bottom_led_set()``
-function.  If you care to play with it, edit
-``src/function/develtest/keys2display.c`` but be sure to recover the original
-before you submit your work.
+To build this, you would normally have to write ``bottom_keyboard_scan()``,
+``bottom_hook_scan()``, ``bottom_show_fixed_msg()``, ``bottom_show_period``,
+``bottom_show_ip4()``, ``bottom_show_ip6()``, ``bottom_show_close_level()``.
+Note that the ``bottom_show_`` routines need not all be implemented fully;
+they exist to permit the top layer to offer information to be laid out in
+a format that is optimal for the target phone.  Do take note that there are
+levels of information, to ensure that shared space on the display is made
+available for higher priority data if need be.
 
 If this works, you are able to scan keys and write texts on the display.
 
@@ -433,20 +503,48 @@ Build 4: Networked console
 In ``make menuconfig``, select the firmware function ``Test network``
 that will provide an LLC-based console over ethernet.
 
-TODO: To build this, you would normally have to write a timer setup and
-interrupt service routine to handle ``bottom_time()`` and
-``bottom_set_timer_set()`` --do not forget to return the old setting for
-the latter-- in addition to the previously written ``bottom_led_set()``
-function.  If you care to play with it, edit
-``src/function/develtest/keys2display.c`` but be sure to recover the original
-before you submit your work.
+To build this, you would normally have to write a driver for the network
+chip.  You would need to handle interrupts from the network interface to
+permit a smoothe operation of the phone.  As part of the driver, you may
+have to locate where the MAC address is in flash -- as that address is
+sometimes loaded from an EEPROM, but on an embedded device it is usually
+cheaper to have the firmware handle that.
+
+If this works, you are able to send and receive information between the
+CPU and the network.
+
+
+Build 5: Echo unit
+------------------
 
-If this works, you are able to use the network; your next build would
-be the bootloader, which is the first real application that goes beyond
-developer toys.
+In ``make menuconfig``, select the firmware function ``Test sound``
+that will echo anything it hears back after a two-second delay.
 
+This is the simplest test for the complete sound interface  It reads
+input signals and sends them back after a delay.  This loop involves
+the microphone for sound reception, DMA for receiving the microphone
+input, the CPU and RAM for the delay, DMA for sending the speaker output,
+and finally the speaker output for playing the sound back at you.
 
-Build 5: Bootloader
+The echo test can toggle between handset mode and speakerphone mode,
+if the phone's hardware supports it.  It will see if the phone is
+off-hook to determine the mode.  Other interfaces (headset and line)
+are not tested, as they are not as vital, and will be much simpler
+to debug after one or two sound paths have shown to work.
+
+To build this, you would normally have to write a driver for the
+sound chip or codec.  This is best done through DMA, as that is the
+best way of assuring a constant playing rate for samples.  An
+approach based on interrupts would suffer from other interrupts and
+critical regions that temporarily disable interrupts.
+
+If this works, the sound parts of your phone appears to be working
+all the way from the CPU to the human operating the phone; your next
+build would be the bootloader, which is the first real application
+that goes beyond developer toys.
+
+
+Build 6: Bootloader
 -------------------
 
 This is the first firmware function that actually reflects a useful
@@ -456,6 +554,16 @@ flash memory.  While the bootloader is active, it will support LAN
 access to the flash memory, even for writing.  Your best bet would be
 to first download the entire contents of flash, of course.
 
+The bootloader is the first target that could actually be useful to
+burn into the phone's flash.  Ideally, this would be done in such a
+place that the original firmware can be re-inserted if so required;
+end-users will be more likely to try your firmerware if it is also
+possible to move back to the original situation that they had.  It
+could be useful if the 0cpm Bootloader could continue to be used
+even under the original firmware, as it gives more control to the
+end-user.  Please document clearly if and how this works on the
+target platform you are porting to.
+
 Note that the bootloader will only run as long as the phone is off-hook;
 if it is on-hook during boot, it will skip to the actual application.
 Given that development machines are usually open, the horn is usually
@@ -464,8 +572,8 @@ be performed with the phone on-hook and so the bootloader would be
 skipped.  This also rules out various abuse patterns.
 
 
-Build 6: SIP phone / doorbell / alarm clock
--------------------------------------------
+Build 7: SIP phone / doorbell / alarm clock / ...
+-------------------------------------------------
 
 You can now select the firmware functions that you are really after.
 
index 22bfcf4..8b30fbc 100644 (file)
@@ -2,6 +2,10 @@ TODO: top_main() can return to invoke "next boot option" cyclically
 TODO: bool bottom_phone_is_offhook (void);
 TODO: TIME_MSEC, TIME_SEC, TIME_MIN, TIME_HOUR, TIME_DAY
 TODO: TIME_BEFORE(a,b)
+TODO: bottom_show_xxx() series of calls
+TODO: bottom_xxx_scan() and top_xxx() from develtests
+TODO: network_can_xxx() will be called before 1500 bytes free but then certainly called again before done
+TODO: bottom_network_send/_recv() accept 32-bit transfers
 
 ----------------------------------------------
 Kernel API between Phone Bottom and Top Halves
diff --git a/include/0cpm/cons.h b/include/0cpm/cons.h
new file mode 100644 (file)
index 0000000..7aa9920
--- /dev/null
@@ -0,0 +1,37 @@
+/* Console logging facility
+ *
+ * This implements logging over an LLC console, where supported.
+ * Messages are written to the log when sent as bottom_printf (fmt, ...)
+ * and will always be prefixed with __FILE__ and __LINE__ information.
+ * The implementation is through a bottom_console_printf() call.
+ * If the console is not compiled in, there will be no code generated
+ * for such calls.
+ *
+ * For portability to all platforms, there must always be at least one
+ * argument, even if it is not used.  There are ways to work around this
+ * constraint, as described on the link below, but relying on it would
+ * be asking for trouble when targetting all kinds of embedded compilers.
+ * http://blog.mellenthin.de/archives/2010/10/26/portable-__va_args__-macros-for-linux-hp-ux-solaris-and-aix/
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+#ifndef HEADER_CONSOLE
+#define HEADER_CONSOLE
+
+#ifndef CONFIG_FUNCTION_DEVEL_NETCONSOLE
+       inline void bottom_printf (char *fmt, ...) { ; }
+#else
+       void bottom_console_vprintf (char *fmt, va_list argh);
+       void bottom_console_printf (char *fmt, ...);
+       inline void bottom_printf (char *fmt, ...) {
+               va_list argh;
+               va_start (argh, fmt);
+               // bottom_console_printf ("%s:%d ", __FILE__, __LINE__);
+               bottom_console_vprintf (fmt, argh);
+               va_end (argh);
+       }
+#endif
+
+#endif
index 696db04..6a199a4 100644 (file)
 extern volatile uint8_t kbdisp;
 asm ("_kbdisp .set 0x666666");
 
+/* The network interface KSZ8842-16 is booted without EEPROM, so it
+ * has no knowledge of anything -- specifically, no MAC address.
+ * It has been hard-wired to a 16-bit interface, but it is addressed
+ * from the DSP chip as a 32-bit interface.  The top half of the
+ * values read are therefore jibberish.  Yes, silly is the right word.
+ */
+#pragma FAR(ksz8842_16)
+extern volatile uint32_t ksz8842_16 [];
+asm ("_ksz8842_16 .set 0x012340");
+
+/* The following are the low-level routines for accessing this
+ * particular use of the KSZ8842-16.  They are used in other
+ * routines, and the definitions in <bottom/ksz8842.h>
+ */
+#define kszreg16(N) ksz8842_16[(N)>>1]
+
+inline uint16_t kszmap16(uint16_t x) {
+       return (x << 8) | (x >> 8);
+}
+
+inline uint32_t kszmap32(uint32_t x) {
+       return (x << 24) | (x >> 24) | ((x << 8) & 0x00ff000) | ((x >> 8) & 0x0000ff00);
+}
+
+inline uint16_t ksz_memget16 (uint8_t *mem) {
+       return (mem [0] << 8) | (mem [1] & 0xff);
+}
+
+inline void ksz_memset16 (uint16_t ksz, uint8_t *mem) {
+       mem [0] = ksz >> 8;
+       mem [1] = ksz & 0xff;
+}
+
+
+#pragma FAR(flash_16)
+extern volatile uint16_t flash_16 [];
+asm ("_flash_16 .set 0x200000");
+
 #endif
 
 
diff --git a/include/bottom/ksz8842.h b/include/bottom/ksz8842.h
new file mode 100644 (file)
index 0000000..0191b23
--- /dev/null
@@ -0,0 +1,100 @@
+/* Micrel KSZ8842 includes.
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+#ifndef HEADER_KSZ8842
+#define HEADER_KSZ8842
+
+
+typedef uint16_t kszint16_t;
+typedef uint32_t kszint32_t;
+
+
+#define KSZ8842_BANKx_BSR      kszreg16(14)
+
+#define KSZ8842_BANK0_BAR      kszreg16(0)
+#define KSZ8842_BANK2_MARL     kszreg16(0)
+#define KSZ8842_BANK2_MARM     kszreg16(2)
+#define KSZ8842_BANK2_MARH     kszreg16(4)
+#define KSZ8842_BANK0_BESR     kszreg16(6)
+
+#define KSZ8842_BANK16_TXCR    kszreg16(0)
+#define KSZ8842_BANK16_TXSR    kszreg16(2)
+#define KSZ8842_BANK16_RXCR    kszreg16(4)
+#define KSZ8842_BANK16_TXMIR   kszreg16(8)
+#define KSZ8842_BANK16_RXMIR   kszreg16(10)
+#define KSZ8842_BANK17_TXQCR   kszreg16(0)
+#define KSZ8842_BANK17_RXQCR   kszreg16(2)
+#define KSZ8842_BANK17_TXFDPR  kszreg16(4)
+#define KSZ8842_BANK17_RXFDPR  kszreg16(6)
+#define KSZ8842_BANK17_QDRL    kszreg16(8)
+#define KSZ8842_BANK17_QDRH    kszreg16(10)
+#define KSZ8842_BANK18_IER     kszreg16(0)
+#define KSZ8842_BANK18_ISR     kszreg16(2)
+#define KSZ8842_BANK18_RXSR    kszreg16(4)
+#define KSZ8842_BANK18_RXBC    kszreg16(6)
+
+#define KSZ8842_BANK32_SIDER   kszreg16(0)
+
+#define KSZ8842_BANK45_P1MBSR  kszreg16(2)
+#define KSZ8842_BANK46_P2MBSR  kszreg16(2)
+
+#define KSZ8842_BANK_SET(x)    (KSZ8842_BANKx_BSR = kszmap16 (x))
+
+
+#define REGVAL_KSZ8842_ISR_LCIS                0x8000
+#define REGVAL_KSZ8842_ISR_TXIS                0x4000
+#define REGVAL_KSZ8842_ISR_RXIS                0x2000
+#define REGVAL_KSZ8842_ISR_RXOIS       0x0800
+#define REGVAL_KSZ8842_ISR_TXPSIE      0x0200
+#define REGVAL_KSZ8842_ISR_RXPSIE      0x0100
+#define REGVAL_KSZ8842_ISR_RXEFIE      0x0080
+
+#define REGVAL_KSZ8842_TXCR_TXFCE      0x0008
+#define REGVAL_KSZ8842_TXCR_TXPE       0x0004
+#define REGVAL_KSZ8842_TXCR_TXCE       0x0002
+#define REGVAL_KSZ8842_TXCR_TXE                0x0001
+
+#define REGVAL_KSZ8842_RXCR_RXFCE      0x0400
+#define REGVAL_KSZ8842_RXCR_RXEFE      0x0200
+#define REGVAL_KSZ8842_RXCR_RXBE       0x0080
+#define REGVAL_KSZ8842_RXCR_RXME       0x0040
+#define REGVAL_KSZ8842_RXCR_RXUE       0x0020
+#define REGVAL_KSZ8842_RXCR_RXRA       0x0010
+#define REGVAL_KSZ8842_RXCR_RXSCE      0x0008
+#define REGVAL_KSZ8842_RXCR_RXE                0x0001
+
+#define REGVAL_KSZ8842_RXSR_RXFV       0x8000
+#define REGVAL_KSZ8842_RXSR_RXBF       0x0080
+#define REGVAL_KSZ8842_RXSR_RXMF       0x0040
+#define REGVAL_KSZ8842_RXSR_RXUF       0x0020
+#define REGVAL_KSZ8842_RXSR_RXFT       0x0008
+#define REGVAL_KSZ8842_RXSR_RXTL       0x0004
+#define REGVAL_KSZ8842_RXSR_RXRF       0x0002
+#define REGVAL_KSZ8842_RXSR_RXCE       0x0001
+
+#define REGVAL_KSZ8842_TXQCR_TXETF     0x0001
+#define REGVAL_KSZ8842_RXQCR_RXRRF     0x0001
+
+#define REGVAL_KSZ8842_PxMBSR_ONLINE   0x0004
+
+#define CTLVAL_KSZ8842_TXIC            0x8000
+
+
+#endif
+
+
+/* Map KSZ8842 network data to or from general purpose memory */
+void ksz_memset16 (kszint16_t ksz, uint8_t *mem);
+kszint16_t ksz_memget16 (uint8_t *mem);
+
+
+/* Setup the KSZ8842 chip */
+void ksz8842_setup_network (void);
+
+/* KSZ8842 interrupt handler, _but_ compiled as a plain function.
+ * Retrieve the ISR register and handle each interrupt reason.
+ */
+void ksz8842_interrupt_handler (void);
index 484330c..ad55dbc 100644 (file)
@@ -48,6 +48,8 @@ extern volatile uint16_t IVPD, IVPH;
 asm ("_IVPD .set 0x0049");
 asm ("_IVPH .set 0x004a");
 
+extern volatile bool tic55x_top_has_been_interrupted;
+
 void setup_interrupts (void);
 
 /* General Purpose I/O registers */
@@ -55,6 +57,30 @@ extern volatile uint16_t ioport IODIR, IODATA;
 asm ("_IODIR .set 0x3400");
 asm ("_IODATA .set 0x3401");
 
+/* EMIF and CE space control registers */
+extern volatile uint16_t ioport EGCR1, EGCR2;
+extern volatile uint16_t ioport CE0_1, CE0_2, CE1_1, CE1_2, CE2_1, CE2_2, CE3_1, CE3_2;
+extern volatile uint16_t ioport CE0_SEC1, CE0_SEC2, CE1_SEC1, CE1_SEC2,
+                                CE2_SEC1, CE2_SEC2, CE3_SEC1, CE3_SEC2;
+asm ("_EGCR1 .set 0x0800");
+asm ("_EGCR2 .set 0x0801");
+asm ("_CE1_1 .set 0x0802");
+asm ("_CE1_2 .set 0x0803");
+asm ("_CE0_1 .set 0x0804");
+asm ("_CE0_2 .set 0x0805");
+asm ("_CE2_1 .set 0x0808");
+asm ("_CE2_2 .set 0x0809");
+asm ("_CE3_1 .set 0x080a");
+asm ("_CE3_2 .set 0x080b");
+asm ("_CE1_SEC1 .set 0x0822");
+asm ("_CE1_SEC2 .set 0x0823");
+asm ("_CE0_SEC1 .set 0x0824");
+asm ("_CE0_SEC2 .set 0x0825");
+asm ("_CE2_SEC1 .set 0x0828");
+asm ("_CE2_SEC2 .set 0x0829");
+asm ("_CE3_SEC1 .set 0x082a");
+asm ("_CE3_SEC2 .set 0x082b");
+
 /* Pin Control Registers for McBSP0, McBSP1 */
 extern volatile uint16_t ioport PCR0, PCR1;
 asm ("_PCR0 .set 0x2812");
@@ -146,5 +172,37 @@ asm ("_PICR .set 0x9400");
 #define REGBIT_PICR_TIM1 1
 #define REGBIT_PICR_TIM0 0
 
+/* PLL configuration registers */
+extern volatile uint16_t ioport PLLCSR, PLLM, PLLDIV1, PLLDIV2, PLLDIV3;
+asm ("_PLLCSR .set 0x1c80");
+asm ("_PLLM .set 0x1c88");
+asm ("_PLLDIV1 .set 0x1c8c");
+asm ("_PLLDIV2 .set 0x1c8e");
+asm ("_PLLDIV3 .set 0x1c90");
+
+#define REGVAL_PLLM_TIMES_2            0x0002
+#define REGVAL_PLLM_TIMES_3            0x0003
+#define REGVAL_PLLM_TIMES_4            0x0004
+#define REGVAL_PLLM_TIMES_5            0x0005
+#define REGVAL_PLLM_TIMES_6            0x0006
+#define REGVAL_PLLM_TIMES_7            0x0007
+#define REGVAL_PLLM_TIMES_8            0x0008
+#define REGVAL_PLLM_TIMES_9            0x0009
+#define REGVAL_PLLM_TIMES_10           0x000a
+#define REGVAL_PLLM_TIMES_11           0x000b
+#define REGVAL_PLLM_TIMES_12           0x000c
+#define REGVAL_PLLM_TIMES_13           0x000d
+#define REGVAL_PLLM_TIMES_14           0x000e
+#define REGVAL_PLLM_TIMES_15           0x000f
+
+#define REGVAL_PLLCSR_PLLEN            0x0001
+#define REGVAL_PLLCSR_PLLRST           0x0008
+#define REGVAL_PLLCSR_STABLE           0x0040
+
+#define REGVAL_PLLDIVx_DxEN            0x8000
+#define REGVAL_PLLDIVx_PLLDIVx_1       0x0000
+#define REGVAL_PLLDIVx_PLLDIVx_2       0x0001
+#define REGVAL_PLLDIVx_PLLDIVx_4       0x0003
+
 
 #endif
index 98274c5..2689bb0 100644 (file)
@@ -1,5 +1,5 @@
 
-objs-bottom-$(CONFIG_TARGET_GRANDSTREAM_BT20x) += src/driver/tic55x/grandstream-bt20x.o src/driver/ht162x.o
+objs-bottom-$(CONFIG_TARGET_GRANDSTREAM_BT20x) += src/driver/tic55x/grandstream-bt20x.o src/driver/ht162x.o src/driver/ksz8842.o
 
 #TODO: replace rts55.lib with bootstrapping in src/driver/tic55x/trampoline.o
 
diff --git a/src/driver/ksz8842.c b/src/driver/ksz8842.c
new file mode 100644 (file)
index 0000000..fde9c62
--- /dev/null
@@ -0,0 +1,225 @@
+/* Micrel KSZ8842-16 drivers.  Probably suitable for -32 as well.
+ *
+ * This driver uses a few low-level routines that should be part
+ * of the architecture definition of a particular phone:
+ *  - kszmap16() maps 16-bit values between CPU and KSZ endianity
+ *  - kszmap32() maps 32-bit values between CPU and KSZ endianity
+ *  - kszreg16(N) is an l-value for 16-bit register N
+ * The registers of this chip are defined in terms of kszreg16()
+ *
+ * The kszmap functions are their own inverse functions.  They are
+ * not applied automatically to the registers; in fact, it is more
+ * efficient to apply them to what is applied to the registers, as
+ * the mappings may be efficiently removed.  For instance, in
+ *
+ * if (KSZ8842_BANK18_ISR & kszmap16(REGVAL_KSZ8842_ISR_LCIS)) ...
+ *
+ * the kszmap16 would not cost anything, as it can be applied at
+ * compile time to the REGVAL_KSZ8842_ISR_LCIS constant.  Variables
+ * that contain values mapped like this would be of type kszint16_t
+ * or kszint32_t; the same as uint16_t and uint32_t, respectively.
+ *
+ * Note that KSZ8842_BANK_SET (N) uses a "normal" uint16_t N.
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <config.h>
+#include <bottom/ksz8842.h>
+
+
+/* What the software knows about the online state of the chip */
+static bool online_status = false;
+
+
+/* KSZ8842 interrupt handler, _but_ compiled as a plain function.
+ * Retrieve the ISR register and handle each interrupt reason.
+ */
+void ksz8842_interrupt_handler (void) {
+       kszint16_t curbank;
+       kszint16_t ifr;
+
+       //
+       // Remember the bank that was active before the interrupt
+       curbank = KSZ8842_BANKx_BSR;
+
+       //
+       // Get hold of the interrupt flags, and clear them
+       KSZ8842_BANK_SET (18);
+       ifr = KSZ8842_BANK18_ISR;
+       KSZ8842_BANK18_ISR = ifr;
+
+       //
+       // See if the link status has changed
+       if (ifr & kszmap16 (REGVAL_KSZ8842_ISR_LCIS)) {
+               // Link change
+               bool either;
+               kszint16_t pxmbsr;
+               KSZ8842_BANK_SET (45);
+               pxmbsr = KSZ8842_BANK45_P1MBSR;
+               KSZ8842_BANK_SET (46);
+               pxmbsr |= KSZ8842_BANK46_P2MBSR;
+               either = (pxmbsr & kszmap16 (REGVAL_KSZ8842_PxMBSR_ONLINE)) != 0;
+               if (either != online_status) {
+                       online_status = either;
+                       if (online_status) {
+                               top_network_online ();
+                       } else {
+                               top_network_offline ();
+                       }
+               }
+       }
+
+       //
+       // See if more frames can be sent or received
+       if (ifr & kszmap16 (REGVAL_KSZ8842_ISR_TXIS)) {
+               top_network_can_send ();
+       }
+       if (ifr & kszmap16 (REGVAL_KSZ8842_ISR_RXIS)) {
+               top_network_can_recv ();
+       }
+
+       //
+       // Ignore a few error conditions after resetting their interrupt flag
+       // kszmap16 (REGVAL_KSZ8842_ISR_RXOIS): RX overrun status
+       // kszmap16 (REGVAL_KSZ8842_ISR_TXPSIE): TX stopped
+       // kszmap16 (REGVAL_KSZ8842_ISR_RXPSIE): RX stopped
+       // kszmap16 (REGVAL_KSZ8842_ISR_RXEFIE): RX frame error
+
+       //
+       // Recover the bank that was active before the interrupt
+       KSZ8842_BANKx_BSR = curbank;
+}
+
+
+/* Try to send a network packet */
+bool bottom_network_send (uint8_t *pkt, uint16_t pktlen) {
+       uint16_t txmem;
+       uint16_t pktidx;
+       KSZ8842_BANK_SET (16);
+       txmem = kszmap16 (KSZ8842_BANK16_TXMIR);
+       if (txmem < (pktlen + 4)) {
+               return false;
+       }
+       KSZ8842_BANK_SET (17);
+       //
+       // Hold while previous send data is being queued
+       while (KSZ8842_BANK17_TXQCR & kszmap16 (REGVAL_KSZ8842_TXQCR_TXETF)) {
+               /* Yes, this is busy waiting.  It should not take long.
+                * Usually, all work will have been done when we get back here.
+                * If not, returning false could cause the network to block.
+                * So it is better to do busy waiting for a short while.
+                */
+               ;
+       }
+       //
+       // First send packet metadata: control, bytecount
+       // TODO: It is possible to set a control-ID in QDRL, see Table 4
+       KSZ8842_BANK17_QDRL = kszmap16 (CTLVAL_KSZ8842_TXIC);
+       KSZ8842_BANK17_QDRH = kszmap16 (pktlen);
+       //
+       // Now write out the data itself
+       pktidx = 0;
+       while (pktidx < pktlen) {
+               KSZ8842_BANK17_QDRL = ksz_memget16 (pkt + pktidx + 0);
+               KSZ8842_BANK17_QDRH = ksz_memget16 (pkt + pktidx + 2);
+               pktidx += 4;
+       }
+       //
+       // Indicate that the data has all been written
+       KSZ8842_BANK17_TXQCR |= kszmap16 (REGVAL_KSZ8842_TXQCR_TXETF);
+       return true;
+}
+
+
+/* Try to receive a network packet */
+bool bottom_network_recv (uint8_t *pkt, uint16_t *pktlen) {
+       bool ok = true;
+       uint16_t rxlen;
+       uint16_t rxdata;
+       //
+       // Hold until previous recv data has been skipped
+       KSZ8842_BANK_SET (17);
+       while (KSZ8842_BANK17_RXQCR & kszmap16 (REGVAL_KSZ8842_RXQCR_RXRRF)) {
+               /* Yes, this is busy waiting.  It should not take long.
+                * Usually, all work will have been done when we get back here.
+                * If not, returning false could cause the network to block.
+                * So it is better to do busy waiting for a short while.
+                */
+               ;
+       }
+       //
+       // Is there a frame ready to be picked up?
+       // Note that only correct frames are welcomed.
+       KSZ8842_BANK_SET (18);
+       if ((KSZ8842_BANK18_RXSR & kszmap16 (REGVAL_KSZ8842_RXSR_RXFV)) == 0) {
+               return false;
+       }
+       //
+       // Check if the frame length does not exceed the available buffer
+       rxlen = (kszmap16 (KSZ8842_BANK18_RXBC) & 0x03ff);
+       ok = ok && (rxlen >= 6 + 6 + 2);
+       ok = ok && (rxlen <= *pktlen);
+       KSZ8842_BANK_SET (17);
+       if (ok) {
+               uint16_t pktidx = 0;
+               *pktlen = rxlen;
+               //
+               // Receive the header info (just skip it)
+               rxdata = KSZ8842_BANK17_QDRL;   // Drop; already got RXSR
+               rxdata = KSZ8842_BANK17_QDRH;   // Drop; already got rxlen
+               //
+               // Receive regular data in 4-byte blocks (possible overshoot up to 3)
+               while (pktidx < rxlen) {
+                       rxdata = KSZ8842_BANK17_QDRL;
+                       ksz_memset16 (rxdata, pkt + pktidx + 0);
+                       rxdata = KSZ8842_BANK17_QDRH;
+                       ksz_memset16 (rxdata, pkt + pktidx + 2);
+                       pktidx += 4;
+               }
+       }
+       //
+       // Indicate that the data has all been read
+       KSZ8842_BANK17_RXQCR |= kszmap16 (REGVAL_KSZ8842_RXQCR_RXRRF);
+       //
+       // Return whether a good packet was actually received
+       return ok;
+}
+
+
+/* Setup the KSZ8842 chip */
+void ksz8842_setup_network (void) {
+       KSZ8842_BANK_SET (2);
+       // TODO: Where is the MAC address stored in Flash?
+       KSZ8842_BANK2_MARH = kszmap16 (0x000b);         // 00:0b:__:__:__:__
+       KSZ8842_BANK2_MARM = kszmap16 (0x8219);         // __:__:82:19:__:__
+       KSZ8842_BANK2_MARL = kszmap16 (0xa0f4);         // __:__:__:__:a0:f4
+       KSZ8842_BANK_SET (17);
+       KSZ8842_BANK17_TXFDPR = kszmap16 (0x4000);      // auto-inc TXQ ptr at 0
+       KSZ8842_BANK17_RXFDPR = kszmap16 (0x4000);      // auto-inc RXQ ptr at 0
+       KSZ8842_BANK_SET (32);
+       KSZ8842_BANK32_SIDER |= kszmap16 (0x0001);      // Switch enable
+       KSZ8842_BANK_SET (16);
+       KSZ8842_BANK16_TXCR =
+               kszmap16 (REGVAL_KSZ8842_TXCR_TXPE) |
+               kszmap16 (REGVAL_KSZ8842_TXCR_TXCE) |
+               kszmap16 (REGVAL_KSZ8842_TXCR_TXE);
+       KSZ8842_BANK16_RXCR =
+               kszmap16 (REGVAL_KSZ8842_RXCR_RXFCE) |
+               kszmap16 (REGVAL_KSZ8842_RXCR_RXBE) |
+               kszmap16 (REGVAL_KSZ8842_RXCR_RXME) |
+               kszmap16 (REGVAL_KSZ8842_RXCR_RXUE) |
+               kszmap16 (REGVAL_KSZ8842_RXCR_RXSCE) |
+               kszmap16 (REGVAL_KSZ8842_RXCR_RXE);
+       KSZ8842_BANK_SET (18);
+       KSZ8842_BANK18_IER |=
+               kszmap16 (REGVAL_KSZ8842_ISR_LCIS) |
+               kszmap16 (REGVAL_KSZ8842_ISR_TXIS) |
+               kszmap16 (REGVAL_KSZ8842_ISR_RXIS);
+       KSZ8842_BANK_SET (0);   // TODO: needless
+}
+
index e69de29..facf645 100644 (file)
@@ -0,0 +1 @@
+/* TODO -- the most-used network chip in SIP phones */
index ab3adf8..82f62b1 100644 (file)
 #include <0cpm/show.h>
 
 #include <bottom/ht162x.h>
+#include <bottom/ksz8842.h>
+
+
+
+/******** EXTERNAL INTERRUPTS SERVICE ROUTINES ********/
+
+
+interrupt void tic55x_int0_isr (void) {
+       tic55x_top_has_been_interrupted = true;
+       ksz8842_interrupt_handler ();
+}
+
+interrupt void tic55x_int1 (void) {
+       tic55x_top_has_been_interrupted = true;
+       //TODO//
+}
+
+interrupt void tic55x_int2 (void) {
+       tic55x_top_has_been_interrupted = true;
+       //TODO//
+}
+
+interrupt void tic55x_int3 (void) {
+       tic55x_top_has_been_interrupted = true;
+       //TODO//
+}
 
 
 /******** HT162x LCD DRIVER LOW-LEVEL FUNCTIONS ********/
@@ -179,8 +205,7 @@ void bottom_led_set (led_idx_t ledidx, led_colour_t col) {
 /* See if the phone (actually, the horn) is offhook */
 bool bottom_phone_is_offhook (void) {
        // The hook switch is attached to GPIO pin 5
-       // return ! tic55x_input_get_bit (REGADDR_IODATA, 5);
-       return (IODATA & (1 << 5)) != 0;
+       return (IODATA & 0x20) != 0;
 }
 
 
@@ -421,6 +446,59 @@ void bottom_show_voicemail (app_level_t level, uint16_t new, uint16_t old) {
 void main (void) {
        uint16_t idx;
        led_colour_t led = LED_STABLE_ON;
+       //
+       // PLL setup: Crystal is 16.384 MHz, increase that a bit
+       //TODO: Possible in theory, but doesn't run and sinusoidal in practice
+       //      At lower PLLM factors, the sine is larger and clips to squarish
+       //TODO// PLLM = REGVAL_PLLM_TIMES_15;
+       //TODO// PLLCSR &= ~REGVAL_PLLCSR_PLLRST;
+       //TODO// while (!(PLLCSR & REGVAL_PLLCSR_STABLE)) {
+       //TODO//        ;
+       //TODO// }
+       //TODO// PLLDIV1 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_4;
+       //TODO// PLLDIV2 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_4;
+       //TODO// PLLDIV3 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_4;
+       //TODO// PLLCSR |= REGVAL_PLLCSR_PLLEN;
+       //
+       // EMIF settings, see SPRU621F, section 2.12 on "EMIF Registers"
+       //
+       // EGCR1 = ...;   // (defaults)
+       // EGCR2 = ...;   // (defaults)
+       // CESCR1 = ...;   // (defaults)
+       // CESCR2 = ...;   // (defaults)
+       //
+       // CE0 selects the network interface
+       // CE0_1 = 0xff03;   // DEFAULT 8-bit async (and defaults)
+       // Fail: CE0_1 = 0xc112;   // 16-bit async, rd setup 2, rd strobe 1, rd hold 2
+       // Fail: CE0_2 = 0x20a2;          // wr setup 2, wr strobe 2, wr hold 2, rd setup 2
+       CE0_1 = 0x3f2f;
+       CE0_2 = 0xffff;
+       // CE0_2 = ...;   // (defaults)
+       // CE0_SC1 = ...;   // (defaults)
+       // CE0_SC2 = ...;   // (defaults)
+       //
+       // CE1 selects the flash chip
+       CE1_1 = 0xff13;   // 16-bit async (and defaults)
+       // CE1_2 = ...;   // (defaults)
+       // CE1_SC1 = ...;   // (defaults)
+       // CE1_SC2 = ...;   // (defaults)
+       //
+       // CE2 selects the SDRAM chips
+       CE2_1 = 0xff33;   // 32-bit SDRAM (and defaults)
+       //TEST// CE2_1 = 0xff13;   // 16-bit async (and defaults)
+       // CE2_2 = ...;   // (defaults)
+       // CE2_SC1 = ...;   // (defaults)
+       // CE2_SC2 = ...;   // (defaults)
+       // Possible: SDC1, SDC2, SDRC1, SDRC2, SDX1, SDX2
+       //
+       // CE3 selects the D-flipflops for keyboard and LCD
+       CE3_1 = 0xff13;   // 16-bit async (and defaults)
+       // CE3_2 = ...;   // (defaults)
+       // CE3_SC1 = ...;   // (defaults)
+       // CE3_SC2 = ...;   // (defaults)
+       //
+       // Further initiation follows
+       //
        for (idx = 0; idx < APP_LEVEL_COUNT; idx++) {
                bt200_level_active [idx] = false;
        }
@@ -432,6 +510,10 @@ void main (void) {
        tic55x_setup_timers ();
        tic55x_setup_interrupts ();
        ht162x_setup_lcd ();
+       ksz8842_setup_network ();
+       // Enable INT0..INT3
+       IER0 |= 0x080c;
+       IER1 |= 0x0001;
        PCR0 = (1 << REGBIT_PCR_XIOEN) | (1 << REGBIT_PCR_RIOEN);
 
 #if 0
index 350a2f5..422f643 100644 (file)
                .ref    _c_int00
                .ref    _tic55x_no_isr
                .ref    _tic55x_tint0_isr
+               .ref    _tic55x_int0_isr
 
 _tic55x_bootld .set    0xff8000
 
 isrmap0:
 resetvect      .ivec   _c_int00, NO_RETA
 nmi            .ivec   _tic55x_no_isr
-int0           .ivec   _tic55x_no_isr
+int0           .ivec   _tic55x_int0_isr
 int2           .ivec   _tic55x_no_isr
 tint0          .ivec   _tic55x_tint0_isr
 rint0          .ivec   _tic55x_no_isr
diff --git a/src/driver/tlv320aic2x.c b/src/driver/tlv320aic2x.c
new file mode 100644 (file)
index 0000000..96f958b
--- /dev/null
@@ -0,0 +1,36 @@
+/* TLV320AIC20/21/24/25/20K/24K codec driver
+ *
+ * The name "codec" is generally used for sound I/O chips that take a
+ * realtime stream of samples and map send them to speakers; and that
+ * do the opposite for microphone input.
+ *
+ * This driver has a generic part, described here, and a lower part
+ * that actually gets the generated byte patterns to and from the chip,
+ * which fits better in the actual phone implementation and/or in the
+ * DSP/CPU chip driver.
+ *
+ * The assumption made in this driver is that there is a single unit
+ * of this codec, and that its outputs are used as prescribed in the
+ * datasheet for headset in/out, handset in/out and handsfree in/out
+ * inasfar as these facilities are configured on the target phone in
+ * HAVE_xxx variables.
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+/* Setup the sound channel to use: SOUND_NONE, _HANDSET, _HEADSET,
+ * _SPEAKER and _LINE.  When set to SOUND_NONE, the chip is in setup
+ * in configuration programming mode; in any other modes, it is setup
+ * in continuous data transfer mode.
+ */
+void bottom_soundchannel (soundchannel_t sch) {
+       //TODO//
+}
+
+/* Setup the communication with the codec, initialise the chip
+ */
+void tls320aic2x_setup_sound (void) {
+       // TODO: Setup codec communications
+       bottom_soundchannel (SOUND_NONE);
+}
index ca7d130..6595bdd 100644 (file)
@@ -75,10 +75,16 @@ config MAINFUNCTION_BOOTLOADER
 
 endchoice
 
+config FUNCTION_SOUND_PLAYER
+       bool "Sound player"
+       help
+         Condfigure the phone as a networked sound device.  This enables
+         computers on the LAN to send sound to the phone to have it played.
+         This is only possible on an IPv6-enabled LAN.
+
 config FUNCTION_DEVEL_NETCONSOLE
        bool "LAN-based console"
        default y if DEVEL=y 
-       default n if DEVEL=n
        help
          Direct console traffic for the main application to the network.
 
index 21ebc72..e0f58fd 100644 (file)
@@ -31,9 +31,24 @@ config MAINFUNCTION_DEVEL_NETWORK
        depends on DEVEL
        select FUNCTION_DEVEL_NETCONSOLE
        help
-         Run a networked console.  In addition to the usual console
+         This test runs a networked console.  In addition to the usual console
          functions, respond to any text sent by mapping it to uppercase.
 
          The details of the networked console are described under the
          add-on function for this console, one menu level up.
 
+config MAINFUNCTION_DEVEL_SOUND
+       bool "Test sound"
+       depends on DEVEL
+       select FUNCTION_DEVEL_SOUNDIO
+       help
+         This test turns your phone into an echo well.  While off-hook, it will
+         echo handset voice to the handset with a one-second delay.  If it is
+         a speakerphone, it will use the built-in microphone and speaker to do
+         the same while on-hook.
+          
+         The sound is usually best implemented through DMA.  Not only does this
+         save lots of interrupts while working on other things, but the reliance
+         on hardware timers and not on any software aspects helps to keep the
+         sample rate very stable, thus leading to better voice quality.
+
index f242217..8fe3c97 100644 (file)
@@ -7,6 +7,8 @@ objs-top-$(CONFIG_MAINFUNCTION_SIP_PHONE) += src/function/sip6phone.o bin/top-ke
 
 objs-top-$(CONFIG_MAINFUNCTION_BOOTLOADER) += src/function/bootload.o bin/top-kernel.o bin/top-simplenet.o function
 
+objs-top-$(CONFIG_FUNCTION_DEVEL_NETCONSOLE) += src/function/netconsole.o
+
 #
 # Test targets, useful during reverse engineering and forward development
 #
@@ -16,5 +18,7 @@ objs-top-$(CONFIG_MAINFUNCTION_DEVEL_TIMER) += src/function/develtest/timer2led.
 
 objs-top-$(CONFIG_MAINFUNCTION_DEVEL_KEYBOARD) += src/function/develtest/keys2display.o
 
-objs-top-$(CONFIG_MAINFUNCTION_DEVEL_NETCONSOLE) += src/function/develtest/netconsole.o
+objs-top-$(CONFIG_MAINFUNCTION_DEVEL_NETWORK) += src/function/develtest/netconsole.o
+
+objs-top-$(CONFIG_MAINFUNCTION_DEVEL_SOUND) += src/function/develtest/echo.o
 
diff --git a/src/function/develtest/echo.c b/src/function/develtest/echo.c
new file mode 100644 (file)
index 0000000..e69de29
index d838ce8..434d5f7 100644 (file)
-/* netconsole.c -- run a console over LLC over ethernet */
+/* netconsole.c -- run a console over LLC over ethernet
+ *
+ * This test program includes a very small (and very selfish)
+ * network stack: It only does LLC2, and welcomes one LAN peer
+ * at a time to connect to SAP 20 where a console is running.
+ *
+ * Console log events are saved up in a buffer, and reported
+ * as soon as the client connects.  From then on, new entries
+ * sent to the console are also sent immediately.  An entry is
+ * made for incoming broadcast messages.
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <stdarg.h>
 
 #include <config.h>
 
 #include <0cpm/cpu.h>
 #include <0cpm/timer.h>
 #include <0cpm/led.h>
+#include <0cpm/kbd.h>
+#include <0cpm/app.h>
+#include <0cpm/show.h>
+#include <0cpm/cons.h>
+
+
+#define onlinetest_top_main top_main
+
+
+bool online = false;
+
+
+void top_timer_expiration (timing_t timeout) {
+       /* Keep the linker happy */ ;
+}
+
+void top_hook_update (bool offhook) {
+       /* Keep the linker happy */ ;
+}
+
+void top_button_press (buttonclass_t bcl, buttoncode_t cde) {
+       /* Keep the linker happy */ ;
+}
+
+void top_button_release (void) {
+       /* Keep the linker happy */ ;
+}
+
+void top_network_online (void) {
+       online = true;
+}
+
+void top_network_offline (void) {
+       online = false;
+}
+
+void top_network_can_send (void) {
+       /* Keep the linker happy */ ;
+}
+
+void top_network_can_recv (void) {
+       /* Keep the linker happy */ ;
+}
+
+
+static bool llc_connected = false;
+static uint8_t peer_sap;
+static uint8_t peer_mac [6];
+static uint8_t llc_ua [6 + 6 + 2 + 3];
+static uint8_t llc_rr [6 + 6 + 2 + 4];
+static uint8_t llc_sent;
+static uint8_t llc_received;
+
+
+/* Dummy LLC2 send routine, ignoring "cnx" as there is just one LLC2 connection.
+ * This is out-only, so N(R) always sends as 0x00 and N(S) increments.
+ * Before sending, the routine will first establish whether the last send
+ * was successful; if not, it will repeat that.  The return value is true if
+ * at least the send was done, relying on future calls to resend if need be.
+ */
+uint8_t llc_pkt [100];
+uint16_t llc_pktlen;
+bool netsend_llc2 (struct llc2 *cnx, uint8_t *data, uint16_t datalen) {
+       bool newpkt;
+       if (datalen > 80) {
+               return false;
+       }
+       if (!llc_connected) {
+               return false;
+       }
+       newpkt = (llc_sent == llc_received);
+       if (newpkt) {
+               // Sending is complete, construct new packet as requested
+               memcpy (llc_pkt +  0, peer_mac, 6);
+               memcpy (llc_pkt +  6, "\x00\x0b\x82\x19\xa0\xf4", 6);
+               llc_pkt [12] = 0x00;
+               llc_pkt [13] = datalen + 4;
+               llc_pkt [14] = peer_sap;                // DSAP
+               llc_pkt [15] = 20;                      // SSAP
+               llc_pkt [16] = llc_sent << 1;           // N(S) = 0x00, information frame
+               llc_pkt [17] = 0x00;                    // N(R) = sent-up-to-here, low bit reset
+               memcpy (llc_pkt + 18, data, datalen);
+               llc_pktlen = 6 + 6 + 2 + 4 + datalen;
+               llc_sent++;
+               llc_sent &= 0x7f;
+       }
+       bottom_network_send (llc_pkt, llc_pktlen);
+       return newpkt;
+}
+
+struct llc2 {
+       uint8_t dummy;
+};
+static struct llc2 llc2_dummy_handle;
+
+/* LLC2-only network packet handling */
+void selfish_llc2_handler (uint8_t *pkt, uint16_t pktlen) {
+       uint16_t typelen;
+       uint16_t cmd;
+       bool ack = false;
+       typelen = (pkt [12] << 8) | pkt [13];
+#if 0
+       if ((pktlen < 14) || (typelen < 46)) {
+               bottom_printf ("Unpadded packet length %d received\n", (unsigned int) pktlen);
+               return;
+       }
+#endif
+       if (typelen > 1500) {
+               // bottom_printf ("Traffic is not LLC but protocol 0x%x\n", (unsigned int) typelen);
+               return;
+       }
+       if ((typelen > 64) && (typelen != pktlen)) {
+               bottom_printf ("Illegal length %d received (pktlen = %d)\n", (unsigned int) typelen, (unsigned int) pktlen);
+               return;
+       }
+       if (pkt [14] != 20) {
+               bottom_printf ("Received LLC traffic for SAP %d instead of 20\n", (unsigned int) pkt [14]);
+               return;
+       }
+       if (llc_connected) {
+               if (memcmp (pkt + 6, peer_mac, 6) != 0) {
+                       // Peer MAC should be the constant while connected
+                       return;
+               }
+               if ((pkt [15] & 0xfe) != peer_sap) {
+                       // Peer SAP should be constant while connected
+                       return;
+               }
+       }
+       cmd = pkt [16];
+       if (typelen > 3) {
+               cmd |= pkt [17] << 8;
+       }
+       if (cmd == 0x007f) {                            // SABME (llc.connect)
+               memcpy (peer_mac, pkt + 6, 6);
+               peer_sap = pkt [15] & 0xfe;
+               llc_sent = llc_received = 0x00;
+               llc_connected = true;
+               netcons_connect (&llc2_dummy_handle);
+               ack = true;
+       } else if (cmd == 0x0053) {                     // DISC  (llc.disconnect)
+               llc_connected = false;
+               netcons_close ();
+               ack = true;
+       } else if ((cmd & 0x0001) == 0x0000) {          // Data sent back (will be ignored)
+               memcpy (llc_rr +  0, peer_mac, 6);
+               memcpy (llc_rr +  6, "\x00\x0b\x82\x19\xa0\xf4", 6);
+                memcpy (llc_rr + 12, "\x00\x04\x00\x15", 4);
+               llc_rr [14] = peer_sap;
+                // memcpy (llc_rr + 12, "\x00\x04\x14\x00", 4);
+               // llc_rr [15] = peer_sap | 0x01;
+               llc_rr [16] = 0x01;                     // supervisory, RR
+               llc_rr [17] = (cmd + 2) & 0xfe;         // outgoing N(R) = incoming N(S) + 1
+               bottom_network_send (llc_rr, sizeof (llc_rr));
+       } else if ((cmd & 0x0007) == 0x0001) {          // Receiver ready / Receiver Reject
+               llc_received = (cmd >> 9);
+       } else {
+               bottom_printf ("Selfishly ignoring LLC traffic with cmd bytes 0x%x 0x%x\n", (uint32_t) pkt [16], (uint32_t) pkt [17]);
+       }
+       if (ack) {
+               memcpy (llc_ua +  0, peer_mac, 6);
+               memcpy (llc_ua +  6, "\x00\x0b\x82\x19\xa0\xf4", 6);
+               memcpy (llc_ua + 12, "\x00\x03\x00\x15\x73", 5);
+               llc_ua [14] = peer_sap;
+               // Try sending; assume repeat will be caused by other/smarter side
+               bottom_network_send (llc_ua, sizeof (llc_ua));
+       }
+}
+
+
+uint8_t netinput [1000];
+uint16_t netinputlen;
 
-void top_main (void) {
+void onlinetest_top_main (void) {
        bottom_critical_region_end ();
+       bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_ON);
        while (true) {
-               TODO: CONSOLE DRIVER OVER LLC
+               if (online) {
+                       bottom_show_fixed_msg (APP_LEVEL_BACKGROUNDED, FIXMSG_READY);
+                       netinputlen = sizeof (netinput);
+                       if (bottom_network_recv (netinput, &netinputlen)) {
+                               if (memcmp (netinput, "\xff\xff\xff\xff\xff\xff", 6) == 0) {
+                                       bottom_printf ("Broadcast!\n");
+#if 0
+                                       bottom_printf ("Broadcast from %x:%x:%x:%x:%x:%x\n",
+                                                       (unsigned int) netinput [ 6],
+                                                       (unsigned int) netinput [ 7],
+                                                       (unsigned int) netinput [ 8],
+                                                       (unsigned int) netinput [ 9],
+                                                       (unsigned int) netinput [10],
+                                                       (unsigned int) netinput [11]);
+#endif
+                               } else {
+                                       bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_OFF);
+                                       selfish_llc2_handler (netinput, netinputlen);
+                               }
+                       }
+               } else {
+                       bottom_show_fixed_msg (APP_LEVEL_BACKGROUNDED, FIXMSG_OFFLINE);
+                       bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_ON);
+               }
        }
 }
 
diff --git a/src/function/netconsole.c b/src/function/netconsole.c
new file mode 100644 (file)
index 0000000..7aa6f76
--- /dev/null
@@ -0,0 +1,177 @@
+/* Network console interface.
+ *
+ * This interface makes it possible to connect to the phone's console
+ * over LLC.  The advantage of LLC is that it works on a LAN only,
+ * and does not require any support of protocols higher than Ethernet.
+ * This makes it suitable to even debug things like IP leases.
+ *
+ * The common interface to these routines can be found in <0cpm/cons.h>
+ * and defines a user-level routine bottom_printf (fmt, ...) which is
+ * a restricted printf() function.  It is mapped to the implementation
+ * of bottom_console_printf() below.
+ *
+ * The network side of this facility uses netcons_connect() and
+ * netcons_close() to notify the console of a LAN host opening and
+ * closing the connection.  The console will send data out using the
+ * commonly available network sending routines.
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <config.h>
+#include <0cpm/cons.h>
+
+
+/*
+ * The entire data and code portion depends on the function NETCONSOLE
+ * being configured through "make menuconfig".
+ */
+#ifdef CONFIG_FUNCTION_DEVEL_NETCONSOLE
+
+
+/******** BUFFER STATIC VARIABLES ********/
+
+// TODO: Configuration option would be nice for CONSBUFLEN (max 64k - 1)
+#define CONSBUFLEN 1024
+
+static char consbuf [CONSBUFLEN];
+
+static uint16_t rpos = CONSBUFLEN, wpos = 0;
+
+
+/******** NETWORK INTERFACE ROUTINES ********/
+
+static struct llc2 *netcons_connection = NULL;
+
+static void trysend (void) {
+       if (netcons_connection) {
+               if (rpos > wpos) {
+                       while (rpos < CONSBUFLEN) {
+                               uint16_t blksz = CONSBUFLEN - rpos;
+                               if (blksz > 80) {
+                                       blksz = 80;
+                               }
+                               if (netsend_llc2 (netcons_connection, consbuf + rpos, blksz)) {
+                                       rpos += blksz;
+                               } else {
+                                       return;
+                               }
+                       }
+                       rpos = 0;
+               }
+               while (rpos < wpos) {
+                       uint16_t blksz = wpos - rpos;
+                       if (blksz > 80) {
+                               blksz = 80;
+                       }
+                       if (netsend_llc2 (netcons_connection, consbuf + rpos, blksz)) {
+                               rpos += blksz;
+                       } else {
+                               return;
+                       }
+               }
+       }
+}
+
+void netcons_connect (struct llc2 *cnx) {
+       netcons_connection = cnx;
+       trysend ();
+}
+
+void netcons_close (void) {
+       netcons_connection = NULL;
+}
+
+
+/******** PRINTING ROUTINES ********/
+
+static const char digits [] = "0123456789abcdef";
+
+static void cons_putchar (char c) {
+       // TODO: Handle buffer full conditions
+       if (wpos >= CONSBUFLEN) {
+               wpos = 0;
+       }
+       consbuf [wpos++] = c;
+}
+
+static void cons_putint (uint32_t val, uint8_t base, uint8_t minpos) {
+       uint32_t divisor = 1;
+       while (minpos-- > 0) {
+               divisor *= base;
+       }
+       while (val / base > divisor) {
+               divisor *= base;
+       }
+       while (divisor > 0) {
+               cons_putchar (digits [(val / divisor)]);
+               val %= divisor;
+               divisor /= base;
+       }
+}
+
+void bottom_console_vprintf (char *fmt, va_list argh) {
+       char *fp = fmt;
+       char *str;
+       char ch;
+       uint32_t intval;
+       while (*fp) {
+               if (*fp != '%') {
+                       cons_putchar (*fp++);
+               } else {
+                       fp++;
+                       switch (*fp++) {
+                       case '\0':
+                               fp--;
+                               break;
+                       case 'c':
+                               ch = va_arg (argh, char);
+                               cons_putchar (ch);
+                               break;
+                       case 's':
+                               str = va_arg (argh, char *);
+                               while (*str) {
+                                       cons_putchar (*str++);
+                               }
+                               break;
+                       case 'd':
+                               intval = (uint32_t) va_arg (argh, unsigned int);
+                               cons_putint (intval, 10, 0);
+                               break;
+                       case 'l':
+                               intval = va_arg (argh, uint32_t);
+                               cons_putint (intval, 10, 0);
+                               break;
+                       case 'p':
+                               intval = (uint32_t) va_arg (argh, void *);
+                               cons_putint (intval, 16, 8);
+                               break;
+                       case 'x':
+                               intval = (uint32_t) va_arg (argh, unsigned int);
+                               cons_putint (intval, 16, 8);
+                               break;
+                       case '%':
+                       default:
+                               cons_putchar (fp [-1]);
+                               break;
+                       }
+               }
+       }
+       trysend ();
+}
+
+void bottom_console_printf (char *fmt, ...) {
+       va_list argh;
+       va_start (argh, fmt);
+       bottom_console_vprintf (fmt, argh);
+       va_end (argh);
+}
+
+
+#endif   /* CONFIG_FUNCTION_DEVEL_NETCONSOLE */