From ec0ee7945ffe8b1484500f18c6e6dc2542cc7997 Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Fri, 1 Nov 2013 10:32:56 +0000 Subject: [PATCH] Store device serial number in EEPROM and arrange to generate uniquely. 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 --- Makefile | 11 ++++++-- main.c | 80 +++++++++++++++++++++++++++++++++++++--------------- setserial.py | 24 ++++++++++++++++ usbconfig.h | 6 ++-- 4 files changed, 94 insertions(+), 27 deletions(-) create mode 100755 setserial.py diff --git a/Makefile b/Makefile index dc72c73..00c04f7 100644 --- 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 a11fbe7..e76a8ce 100644 --- a/main.c +++ b/main.c @@ -29,10 +29,22 @@ #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 index 0000000..abe9612 --- /dev/null +++ b/setserial.py @@ -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()) diff --git a/usbconfig.h b/usbconfig.h index 86fdc69..1060500 100644 --- a/usbconfig.h +++ b/usbconfig.h @@ -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 -- 2.23.0