Store device serial number in EEPROM and arrange to generate uniquely.
authorPat Thoyts <patthoyts@users.sourceforge.net>
Fri, 1 Nov 2013 10:32:56 +0000 (10:32 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Fri, 1 Nov 2013 10:32:56 +0000 (10:32 +0000)
Use the EEMEM attribute to generate an eeprom specific hex file which we
then modify using a python script to configure unique serial numbers.
Also, avoid writing the calibration value unless it has changed to extend
the device lifetime as eeprom is limited to around 100K writes.

Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
Makefile
main.c
setserial.py [new file with mode: 0755]
usbconfig.h

index dc72c7367bbf51a808ce825c3795533fa08ebd1a..00c04f7f5b0d32ab6b342492cec8793692b1d263 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,6 @@ all:  main.hex
 flash: all
        $(AVRDUDE) -U flash:w:main.hex:i
 
-
 # Fuse high byte:
 # 0xdd = 1 1 0 1   1 1 0 1
 #        ^ ^ ^ ^   ^ \-+-/ 
@@ -85,12 +84,20 @@ main.bin:   $(OBJECTS)
        $(COMPILE) -o main.bin $(OBJECTS)
 
 main.hex: main.bin
-       @rm -f main.hex main.eep.hex
+       @rm -f main.hex
        @avr-objcopy -j .text -j .data -O ihex main.bin main.hex
        @$(SHELL) checksize main.bin 4096 256
 # do the checksize script as our last action to allow successful compilation
 # on Windows with WinAVR where the Unix commands will fail.
 
+serial: main.bin
+       @avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+         --change-section-lma .eeprom=0 -O ihex main.bin main.eep.hex
+       @python setserial.py main.eep.hex
+
+flash_serial:
+       $(AVRDUDE) -U eeprom:w:main.eep.hex:i
+
 disasm:        main.bin
        avr-objdump -d main.bin
 
diff --git a/main.c b/main.c
index a11fbe7ec64c46e465ec78175090c4037940b56c..e76a8ce91d06b505bbaf7cb8a3df4e857c001247 100644 (file)
--- a/main.c
+++ b/main.c
 
 #include "descriptor.c"
 
-/* one byte reserved for oscillator calibration */
-#define CONFIG_TOP ((uint8_t *)1)
+/* one byte reserved for oscillator calibration and 6 bytes for serial number */
+#define CONFIG_TOP ((uint8_t *)1+6)
 #define CONFIG_LEN 128
 
+char EEMEM ee_serial[USB_CFG_SERIAL_NUMBER_LEN] = { USB_CFG_SERIAL_NUMBER };
+uint8_t EEMEM ee_calibration = 0xff;
+
+/* Declare space in RAM for the default serial number. This should
+ * be overwrittern from eeprom before USB gets initialized
+ */
+int usbDescriptorStringSerialNumber[USB_CFG_SERIAL_NUMBER_LEN + 2] = {
+    USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN),
+    USB_CFG_SERIAL_NUMBER,
+    0
+};
+
 enum {
     Modifier_NumLock = 1,
     Modifier_CapsLock = 2,
@@ -82,9 +94,12 @@ typedef struct Global_t {
     uchar state;
     uchar LED_state;
     uchar idle_rate;
+    uint8_t calibrationValue;
     keyboard_report_t keyboard_report;
 } Global_t;
-static Global_t Global = {0, Led_Slow, 0, 0, 0xff, 0, {0, 0, {0, 0, 0, 0, 0, 0}}};
+static Global_t Global = {
+    0, Led_Slow, 0, 0, 0xff, 0, 0xff, {0, 0, {0, 0, 0, 0, 0, 0}}
+};
 
 /* called when the PC sets the LED state, we can set
  * set the caps lock led indicator
@@ -223,35 +238,54 @@ appPoll(void)
 void
 on_usb_reset(void)
 {
-       calibrateOscillator();
+    calibrateOscillator();
 #if F_CPU==16500000
-       eeprom_write_byte(0, OSCCAL);   /* store the calibrated value in EEPROM */
+    /* store the calibrated value in EEPROM only if changed */
+    if (Global.calibrationValue != OSCCAL) {
+        eeprom_write_byte(&ee_calibration, OSCCAL);
+        Global.calibrationValue = OSCCAL;
+    }
 #endif
 }
 
+static void
+load_serial_eeprom()
+{
+    uchar serial[USB_CFG_SERIAL_NUMBER_LEN + 2];
+    serial[0] = 0xff;
+    eeprom_read_block(&serial, &ee_serial, USB_CFG_SERIAL_NUMBER_LEN);
+    if (serial[0] != 0xff) {
+        int n;
+        for (n = 0; n < USB_CFG_SERIAL_NUMBER_LEN; ++n) {
+            usbDescriptorStringSerialNumber[1 + n] = (int)(serial[n]);
+        }
+    }
+}
+
 int __attribute__((noreturn))
 main(void)
 {
 #if F_CPU==16500000
-       uint8_t calibrationValue;
-       calibrationValue = eeprom_read_byte(0); /* calibration value from last time */
-       if(calibrationValue != 0xff){
-               OSCCAL = calibrationValue;
-       }
+    /* read the calibration value from last time */
+    Global.calibrationValue = eeprom_read_byte(&ee_calibration);
+    if (Global.calibrationValue != 0xff){
+        OSCCAL = Global.calibrationValue;
+    }
 #endif
 
-       wdt_enable(WDTO_1S);   /* setup a 1 second watchdog */
-       odDebugInit();
-       appInit();             /* app specific initialization */
-       usbInit();             /* usb library initialization */
-       usbDeviceDisconnect(); /* enforce re-enumeration */
-    _delay_ms(300);
-       usbDeviceConnect();
+    load_serial_eeprom();  /* load the serial number from eeprom[1..7] */
+    wdt_enable(WDTO_1S);   /* setup a 1 second watchdog */
+    odDebugInit();
+    appInit();             /* app specific initialization */
+    usbInit();             /* usb library initialization */
+    usbDeviceDisconnect(); /* enforce re-enumeration */
+    _delay_ms(400);
+    usbDeviceConnect();
     PORTB &= ~(_BV(PB1));  /* toggle the LED */
-       sei();
-       for(;;) {
-               wdt_reset();       /* reset the watchdog timer */
-               usbPoll();
-               appPoll();
-       }
+    sei();
+    for(;;) {
+        wdt_reset();       /* reset the watchdog timer */
+        usbPoll();
+        appPoll();
+    }
 }
diff --git a/setserial.py b/setserial.py
new file mode 100755 (executable)
index 0000000..abe9612
--- /dev/null
@@ -0,0 +1,24 @@
+import sys,os
+from intelhex import IntelHex
+
+def main(argv = None):
+    if (argv is None):
+        argv = sys.argv
+    data = IntelHex(argv[1])
+    if not os.path.exists('.lastnum'):
+        with open('.lastnum','w') as f:
+            f.write("K00000")
+    with open('.lastnum','r+') as last:
+        line = last.readline()
+        num = int(line[1:]) + 1
+        serial = "K%05d" % num
+        print "serial number %s" % serial
+        data.puts(1, serial)
+        with open(argv[1], 'w') as f:
+            data.write_hex_file(f)
+        last.seek(0)
+        last.write(serial)
+    return 0
+
+if __name__=='__main__':
+    sys.exit(main())
index 86fdc69a4c03a42c5b2bc91fe99a6969d3f948c1..1060500e2f99f7f44dcd43943c6ba70892cf1fa4 100644 (file)
@@ -260,8 +260,10 @@ extern void    on_usb_reset(void);
  * the macros. See the file USB-IDs-for-free.txt before you assign a name if
  * you use a shared VID/PID.
  */
-#define USB_CFG_SERIAL_NUMBER   'K', '0', '0', '0', '0', '1'
+
+#define USB_CFG_SERIAL_NUMBER   'Z', '0', '0', '0', '0', '0'
 #define USB_CFG_SERIAL_NUMBER_LEN   6
+
 /* Same as above for the serial number. If you don't want a serial number,
  * undefine the macros.
  * It may be useful to provide the serial number through other means than at
@@ -356,7 +358,7 @@ extern void    on_usb_reset(void);
 #define USB_CFG_DESCR_PROPS_STRING_0                0
 #define USB_CFG_DESCR_PROPS_STRING_VENDOR           0
 #define USB_CFG_DESCR_PROPS_STRING_PRODUCT          0
-#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER    0
+#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER    (USB_PROP_IS_RAM | (2 * USB_CFG_SERIAL_NUMBER_LEN + 2))
 #define USB_CFG_DESCR_PROPS_HID                     0
 #define USB_CFG_DESCR_PROPS_HID_REPORT              0
 #define USB_CFG_DESCR_PROPS_UNKNOWN                 0