1 /*!
2 \file usb_iap_core.c
3 \brief IAP driver
4
5 \version 2020-08-04, V1.1.0, firmware for GD32VF103
6 \version 2020-12-11, V1.1.1, firmware for GD32VF103
7 */
8
9 /*
10 Copyright (c) 2020, GigaDevice Semiconductor Inc.
11
12 Redistribution and use in source and binary forms, with or without modification,
13 are permitted provided that the following conditions are met:
14
15 1. Redistributions of source code must retain the above copyright notice, this
16 list of conditions and the following disclaimer.
17 2. Redistributions in binary form must reproduce the above copyright notice,
18 this list of conditions and the following disclaimer in the documentation
19 and/or other materials provided with the distribution.
20 3. Neither the name of the copyright holder nor the names of its contributors
21 may be used to endorse or promote products derived from this software without
22 specific prior written permission.
23
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
33 OF SUCH DAMAGE.
34 */
35
36 #include "usb_iap_core.h"
37 #include "flash_operation.h"
38 #include <string.h>
39
40 #define USBD_VID 0x28E9U
41 #define USBD_PID 0x0228U
42
43 /* Note:it should use the C99 standard when compiling the below codes */
44 /* USB standard device descriptor */
45 __ALIGN_BEGIN const usb_desc_dev iap_dev_desc __ALIGN_END =
46 {
47 .header =
48 {
49 .bLength = USB_DEV_DESC_LEN,
50 .bDescriptorType = USB_DESCTYPE_DEV
51 },
52 .bcdUSB = 0x0200U,
53 .bDeviceClass = 0x00U,
54 .bDeviceSubClass = 0x00U,
55 .bDeviceProtocol = 0x00U,
56 .bMaxPacketSize0 = USB_FS_EP0_MAX_LEN,
57 .idVendor = USBD_VID,
58 .idProduct = USBD_PID,
59 .bcdDevice = 0x0100U,
60 .iManufacturer = STR_IDX_MFC,
61 .iProduct = STR_IDX_PRODUCT,
62 .iSerialNumber = STR_IDX_SERIAL,
63 .bNumberConfigurations = USBD_CFG_MAX_NUM
64 };
65
66 __ALIGN_BEGIN const usb_hid_desc_config_set iap_config_desc __ALIGN_END =
67 {
68 .config =
69 {
70 .header =
71 {
72 .bLength = sizeof(usb_desc_config),
73 .bDescriptorType = USB_DESCTYPE_CONFIG
74 },
75 .wTotalLength = USB_DESC_LEN_IAP_CONFIG_SET,
76 .bNumInterfaces = 0x01U,
77 .bConfigurationValue = 0x01U,
78 .iConfiguration = 0x00U,
79 .bmAttributes = 0x80U,
80 .bMaxPower = 0x32U
81 },
82
83 .hid_itf =
84 {
85 .header =
86 {
87 .bLength = sizeof(usb_desc_itf),
88 .bDescriptorType = USB_DESCTYPE_ITF
89 },
90 .bInterfaceNumber = 0x00U,
91 .bAlternateSetting = 0x00U,
92 .bNumEndpoints = 0x02U,
93 .bInterfaceClass = USB_HID_CLASS,
94 .bInterfaceSubClass = 0x00U,
95 .bInterfaceProtocol = 0x00U,
96 .iInterface = 0x00U
97 },
98
99 .hid_vendor =
100 {
101 .header =
102 {
103 .bLength = sizeof(usb_desc_hid),
104 .bDescriptorType = USB_DESCTYPE_HID
105 },
106 .bcdHID = 0x0111U,
107 .bCountryCode = 0x00U,
108 .bNumDescriptors = 0x01U,
109 .bDescriptorType = USB_DESCTYPE_REPORT,
110 .wDescriptorLength = USB_DESC_LEN_IAP_REPORT,
111 },
112
113 .hid_epin =
114 {
115 .header =
116 {
117 .bLength = sizeof(usb_desc_ep),
118 .bDescriptorType = USB_DESCTYPE_EP
119 },
120 .bEndpointAddress = IAP_IN_EP,
121 .bmAttributes = USB_EP_ATTR_INT,
122 .wMaxPacketSize = IAP_IN_PACKET,
123 .bInterval = 0x01U
124 },
125
126 .hid_epout =
127 {
128 .header =
129 {
130 .bLength = sizeof(usb_desc_ep),
131 .bDescriptorType = USB_DESCTYPE_EP
132 },
133 .bEndpointAddress = IAP_OUT_EP,
134 .bmAttributes = USB_EP_ATTR_INT,
135 .wMaxPacketSize = IAP_OUT_PACKET,
136 .bInterval = 0x01U
137 }
138 };
139
140 /* USB language ID Descriptor */
141 static __ALIGN_BEGIN const usb_desc_LANGID usbd_language_id_desc __ALIGN_END =
142 {
143 .header =
144 {
145 .bLength = sizeof(usb_desc_LANGID),
146 .bDescriptorType = USB_DESCTYPE_STR
147 },
148 .wLANGID = ENG_LANGID
149 };
150
151 /* USB manufacture string */
152 static __ALIGN_BEGIN const usb_desc_str manufacturer_string __ALIGN_END =
153 {
154 .header =
155 {
156 .bLength = USB_STRING_LEN(10U),
157 .bDescriptorType = USB_DESCTYPE_STR,
158 },
159 .unicode_string = {'G', 'i', 'g', 'a', 'D', 'e', 'v', 'i', 'c', 'e'}
160 };
161
162 /* USB product string */
163 static __ALIGN_BEGIN const usb_desc_str product_string __ALIGN_END =
164 {
165 .header =
166 {
167 .bLength = USB_STRING_LEN(12U),
168 .bDescriptorType = USB_DESCTYPE_STR,
169 },
170 .unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'I', 'A', 'P'}
171 };
172
173 /* USBD serial string */
174 static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END =
175 {
176 .header =
177 {
178 .bLength = USB_STRING_LEN(2U),
179 .bDescriptorType = USB_DESCTYPE_STR,
180 }
181 };
182
183 void *const usbd_iap_strings[] =
184 {
185 [STR_IDX_LANGID] = (uint8_t *)&usbd_language_id_desc,
186 [STR_IDX_MFC] = (uint8_t *)&manufacturer_string,
187 [STR_IDX_PRODUCT] = (uint8_t *)&product_string,
188 [STR_IDX_SERIAL] = (uint8_t *)&serial_string
189 };
190
191 usb_desc iap_desc = {
192 .dev_desc = (uint8_t *)&iap_dev_desc,
193 .config_desc = (uint8_t *)&iap_config_desc,
194 .strings = usbd_iap_strings
195 };
196
197 /* local function prototypes ('static') */
198 static uint8_t iap_init (usb_dev *udev, uint8_t config_index);
199 static uint8_t iap_deinit (usb_dev *udev, uint8_t config_index);
200 static uint8_t iap_req_handler (usb_dev *udev, usb_req *req);
201 static uint8_t iap_data_out (usb_dev *udev, uint8_t ep_num);
202
203 /* IAP requests management functions */
204 static void iap_req_erase (usb_dev *udev);
205 static void iap_req_dnload (usb_dev *udev);
206 static void iap_req_optionbyte(usb_dev *udev, uint8_t option_num);
207 static void iap_req_leave (usb_dev *udev);
208 static void iap_address_send (usb_dev *udev);
209
210 usb_class_core iap_class = {
211 .init = iap_init,
212 .deinit = iap_deinit,
213 .req_proc = iap_req_handler,
214 .data_out = iap_data_out
215 };
216
217 /* USB custom HID device report descriptor */
218 __ALIGN_BEGIN const uint8_t iap_report_desc[USB_DESC_LEN_IAP_REPORT] __ALIGN_END =
219 {
220 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
221 0x09, 0x00, /* USAGE (Custom Device) */
222 0xa1, 0x01, /* COLLECTION (Application) */
223
224 /* IAP command and data */
225 0x85, 0x01, /* REPORT_ID (0x01) */
226 0x09, 0x01, /* USAGE (IAP command) */
227 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
228 0x25, 0xff, /* LOGICAL_MAXIMUM (255) */
229 0x75, 0x08, /* REPORT_SIZE (8) */
230 0x95, REPORT_OUT_COUNT,
231 0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
232
233 /* device status and option byte */
234 0x85, 0x02, /* REPORT_ID (0x02) */
235 0x09, 0x02, /* USAGE (Status and option byte) */
236 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
237 0x25, 0xff, /* LOGICAL_MAXIMUM (255) */
238 0x75, 0x08, /* REPORT_SIZE (8) */
239 0x95, REPORT_IN_COUNT, /* REPORT_COUNT (23) */
240 0x81, 0x82, /* INPUT (Data,Var,Abs,Vol) */
241
242 0xc0 /* END_COLLECTION */
243 };
244
245 /*!
246 \brief send iap report
247 \param[in] udev: pointer to USB device instance
248 \param[in] report: pointer to HID report
249 \param[in] len: data length
250 \param[out] none
251 \retval USB device operation status
252 */
iap_report_send(usb_dev * udev,uint8_t * report,uint32_t len)253 uint8_t iap_report_send (usb_dev *udev, uint8_t *report, uint32_t len)
254 {
255 usbd_ep_send (udev, IAP_IN_EP, report, len);
256
257 return USBD_OK;
258 }
259
260 /*!
261 \brief initialize the IAP device
262 \param[in] udev: pointer to USB device instance
263 \param[in] config_index: configuration index
264 \param[out] none
265 \retval USB device operation status
266 */
iap_init(usb_dev * udev,uint8_t config_index)267 static uint8_t iap_init (usb_dev *udev, uint8_t config_index)
268 {
269 static __ALIGN_BEGIN usbd_iap_handler iap_handler __ALIGN_END;
270
271 /* initialize Tx endpoint */
272 usbd_ep_setup(udev, &(iap_config_desc.hid_epin));
273
274 /* initialize Rx endpoint */
275 usbd_ep_setup(udev, &(iap_config_desc.hid_epout));
276
277 /* unlock the internal flash */
278 fmc_unlock();
279
280 memset((void *)&iap_handler, 0U, sizeof(usbd_iap_handler));
281
282 /* prepare receive data */
283 usbd_ep_recev(udev, IAP_OUT_EP, iap_handler.report_buf, IAP_OUT_PACKET);
284
285 iap_handler.base_address = APP_LOADED_ADDR;
286
287 udev->dev.class_data[USBD_IAP_INTERFACE] = (void *)&iap_handler;
288
289 return USBD_OK;
290 }
291
292 /*!
293 \brief de-initialize the iap device
294 \param[in] udev: pointer to USB device instance
295 \param[in] config_index: configuration index
296 \param[out] none
297 \retval USB device operation status
298 */
iap_deinit(usb_dev * udev,uint8_t config_index)299 static uint8_t iap_deinit (usb_dev *udev, uint8_t config_index)
300 {
301 /* deinitialize iap endpoints */
302 usbd_ep_clear (udev, IAP_IN_EP);
303 usbd_ep_clear (udev, IAP_OUT_EP);
304
305 /* lock the internal flash */
306 fmc_lock();
307
308 return USBD_OK;
309 }
310
311 /*!
312 \brief handle the iap class-specific requests
313 \param[in] udev: pointer to USB device instance
314 \param[in] req: device class-specific request
315 \param[out] none
316 \retval USB device operation status
317 */
iap_req_handler(usb_dev * udev,usb_req * req)318 static uint8_t iap_req_handler (usb_dev *udev, usb_req *req)
319 {
320 usb_transc *transc = &udev->dev.transc_in[0];
321
322 usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
323
324 switch (req->bRequest) {
325 case GET_REPORT:
326 /* no use for this driver */
327 break;
328
329 case GET_IDLE:
330 transc->xfer_buf = (uint8_t *)&iap->idlestate;
331 transc->remain_len = 1U;
332 break;
333
334 case GET_PROTOCOL:
335 transc->xfer_buf = (uint8_t *)&iap->protocol;
336 transc->remain_len = 1U;
337 break;
338
339 case SET_REPORT:
340 iap->reportID = (uint8_t)(req->wValue);
341 break;
342
343 case SET_IDLE:
344 iap->idlestate = (uint8_t)(req->wValue >> 8U);
345 break;
346
347 case SET_PROTOCOL:
348 iap->protocol = (uint8_t)(req->wValue);
349 break;
350
351 case USB_GET_DESCRIPTOR:
352 if (USB_DESCTYPE_REPORT == (req->wValue >> 8U)) {
353 transc->remain_len = USB_MIN(USB_DESC_LEN_IAP_REPORT, req->wLength);
354 transc->xfer_buf = (uint8_t *)iap_report_desc;
355 }
356 break;
357
358 default:
359 return USBD_FAIL;
360 }
361
362 return USBD_OK;
363 }
364
365 /*!
366 \brief handle data out stage
367 \param[in] udev: pointer to USB device instance
368 \param[in] ep_num: endpoint identifier
369 \param[out] none
370 \retval none
371 */
iap_data_out(usb_dev * udev,uint8_t ep_num)372 static uint8_t iap_data_out (usb_dev *udev ,uint8_t ep_num)
373 {
374 usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
375
376 if (0x01U == iap->report_buf[0]) {
377 switch (iap->report_buf[1]) {
378 case IAP_DNLOAD:
379 iap_req_dnload(udev);
380 break;
381
382 case IAP_ERASE:
383 iap_req_erase(udev);
384 break;
385
386 case IAP_OPTION_BYTE1:
387 iap_req_optionbyte(udev, 0x01U);
388 break;
389
390 case IAP_LEAVE:
391 iap_req_leave(udev);
392 break;
393
394 case IAP_GETBIN_ADDRESS:
395 iap_address_send(udev);
396 break;
397
398 case IAP_OPTION_BYTE2:
399 iap_req_optionbyte(udev, 0x02U);
400 break;
401
402 default:
403 break;
404 }
405 }
406
407 usbd_ep_recev(udev, IAP_OUT_EP, iap->report_buf, IAP_OUT_PACKET);
408
409 return USBD_OK;
410 }
411
412 /*!
413 \brief handle the IAP_DNLOAD request
414 \param[in] udev: pointer to usb device instance
415 \param[out] none
416 \retval none
417 */
iap_req_dnload(usb_dev * udev)418 static void iap_req_dnload(usb_dev *udev)
419 {
420 usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
421
422 if (0U != iap->transfer_times) {
423 if (1U == iap->transfer_times) {
424 if (0U == iap->lps) {
425 iap_data_write(&iap->report_buf[2], iap->base_address, TRANSFER_SIZE);
426 } else {
427 iap_data_write(&iap->report_buf[2], iap->base_address, iap->file_length % TRANSFER_SIZE);
428 iap->lps = 0U;
429 }
430
431 iap->dev_status[0] = 0x02U;
432 iap->dev_status[1] = 0x02U;
433 iap_report_send (udev, iap->dev_status, IAP_IN_PACKET);
434 } else {
435 iap_data_write(&iap->report_buf[2], iap->base_address, TRANSFER_SIZE);
436
437 iap->base_address += TRANSFER_SIZE;
438 }
439
440 iap->transfer_times--;
441 }
442 }
443
444 /*!
445 \brief handle the IAP_ERASE request
446 \param[in] udev: pointer to usb device instance
447 \param[out] none
448 \retval none
449 */
iap_req_erase(usb_dev * udev)450 static void iap_req_erase(usb_dev *udev)
451 {
452 uint32_t addr = 0U;
453
454 usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
455
456 /* get base address to erase */
457 iap->base_address = iap->report_buf[2];
458 iap->base_address |= iap->report_buf[3] << 8U;
459 iap->base_address |= iap->report_buf[4] << 16U;
460 iap->base_address |= iap->report_buf[5] << 24U;
461
462 /* get file length */
463 iap->file_length = iap->report_buf[7];
464 iap->file_length |= iap->report_buf[8] << 8U;
465 iap->file_length |= iap->report_buf[9] << 16U;
466 iap->file_length |= iap->report_buf[10] << 24U;
467
468 iap->lps = iap->file_length % TRANSFER_SIZE;
469 if (0U == iap->lps) {
470 iap->transfer_times = iap->file_length / TRANSFER_SIZE;
471 } else {
472 iap->transfer_times = iap->file_length / TRANSFER_SIZE + 1U;
473 }
474
475 /* check if the address is in protected area */
476 if (IS_PROTECTED_AREA(iap->base_address)) {
477 return;
478 }
479
480 addr = iap->base_address;
481
482 /* unlock the flash program erase controller */
483 fmc_unlock();
484
485 flash_erase(addr, iap->file_length, iap->report_buf);
486
487 fmc_lock();
488
489 iap->dev_status[0] = 0x02U;
490 iap->dev_status[1] = 0x01U;
491
492 usbd_ep_send(udev, IAP_IN_EP, iap->dev_status, IAP_IN_PACKET);
493 }
494
495 /*!
496 \brief handle the IAP_OPTION_BYTE request
497 \param[in] udev: pointer to USB device instance
498 \param[in] option_num: number of option byte
499 \param[out] none
500 \retval none
501 */
iap_req_optionbyte(usb_dev * udev,uint8_t option_num)502 static void iap_req_optionbyte(usb_dev *udev, uint8_t option_num)
503 {
504 uint8_t i = 0U;
505 uint32_t address = 0U;
506
507 usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
508
509 iap->option_byte[0]= 0x02U;
510
511 if (0x01U == option_num) {
512 address = OPT_BYTE_ADDR1;
513 #ifdef OPT_BYTE_ADDR2
514 } else if (0x02U == option_num) {
515 address = OPT_BYTE_ADDR2;
516 #endif
517 } else {
518 return;
519 }
520
521 for (i = 1U; i < 17U; i++) {
522 iap->option_byte[i] = *(uint8_t *)address;
523 address++;
524 }
525
526 iap_report_send (udev, iap->option_byte, IAP_IN_PACKET);
527 }
528
529 /*!
530 \brief handle the IAP_LEAVE request
531 \param[in] udev: pointer to usb device instance
532 \param[out] none
533 \retval none
534 */
iap_req_leave(usb_dev * udev)535 static void iap_req_leave(usb_dev *udev)
536 {
537 /* lock the internal flash */
538 fmc_lock();
539
540 /* generate system reset to allow jumping to the user code */
541 eclic_system_reset();
542 }
543
544 /*!
545 \brief handle the IAP_SEND_ADDRESS request
546 \param[in] udev: pointer to usb device instance
547 \param[out] none
548 \retval none
549 */
iap_address_send(usb_dev * udev)550 static void iap_address_send(usb_dev *udev)
551 {
552 usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
553
554 iap->bin_addr[0] = 0x02U;
555
556 iap->bin_addr[1] = (uint8_t)(APP_LOADED_ADDR);
557 iap->bin_addr[2] = (uint8_t)(APP_LOADED_ADDR >> 8U);
558 iap->bin_addr[3] = (uint8_t)(APP_LOADED_ADDR >> 16U);
559 iap->bin_addr[4] = (uint8_t)(APP_LOADED_ADDR >> 24U);
560
561 iap_report_send (udev, iap->bin_addr, IAP_IN_PACKET);
562 }
563