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