LCOV - code coverage report
Current view: top level - /__w/zephyr/zephyr/lib/midi2 - ump_stream_responder.c Coverage Total Hit
Test: new.info Lines: 58.3 % 12 7
Test Date: 2025-10-25 14:54:41

            Line data    Source code
       1            0 : /*
       2              :  * Copyright (c) 2025 Titouan Christophe
       3              :  * SPDX-License-Identifier: Apache-2.0
       4              :  */
       5              : 
       6              : #include <string.h>
       7              : #include <zephyr/devicetree.h>
       8              : #include <zephyr/drivers/hwinfo.h>
       9              : #include <zephyr/sys/byteorder.h>
      10              : 
      11              : #include "ump_stream_responder.h"
      12              : 
      13            0 : #define BIT_IF(cond, n) ((cond) ? BIT(n) : 0)
      14              : 
      15              : /**
      16              :  * @brief  MIDI-CI version identifier for UMP v1.1 devices
      17              :  * @see ump112: 7.1.8 FB Info Notification > MIDI-CI Message Version/Format
      18              :  */
      19            1 : #define MIDI_CI_VERSION_FORMAT_UMP_1_1 0x01
      20              : 
      21            0 : static inline bool ep_has_midi1(const struct ump_endpoint_dt_spec *ep)
      22              : {
      23              :         for (size_t i = 0; i < ep->n_blocks; i++) {
      24              :                 if (ep->blocks[i].is_midi1) {
      25              :                         return true;
      26              :                 }
      27              :         }
      28              :         return false;
      29              : }
      30              : 
      31            0 : static inline bool ep_has_midi2(const struct ump_endpoint_dt_spec *ep)
      32              : {
      33              :         for (size_t i = 0; i < ep->n_blocks; i++) {
      34              :                 if (!ep->blocks[i].is_midi1) {
      35              :                         return true;
      36              :                 }
      37              :         }
      38              :         return false;
      39              : }
      40              : 
      41              : /**
      42              :  * @brief  Build an Endpoint Info Notification Universal MIDI Packet
      43              :  * @see ump112: 7.1.2 Endpoint Info Notification Message
      44              :  */
      45            1 : static inline struct midi_ump make_endpoint_info(const struct ump_endpoint_dt_spec *ep)
      46              : {
      47              :         struct midi_ump res;
      48              : 
      49              :         res.data[0] = (UMP_MT_UMP_STREAM << 28)
      50              :                     | (UMP_STREAM_STATUS_EP_INFO << 16)
      51              :                     | 0x0101; /* UMP version 1.1 */
      52              : 
      53              :         res.data[1] = BIT(31) /* Static function blocks */
      54              :                     | ((ep->n_blocks) << 24)
      55              :                     | BIT_IF(ep_has_midi2(ep), 9)
      56              :                     | BIT_IF(ep_has_midi1(ep), 8);
      57              : 
      58              :         return res;
      59              : }
      60              : 
      61              : /**
      62              :  * @brief  Build a Function Block Info Notification Universal MIDI Packet
      63              :  * @see ump112: 7.1.8 Function Block Info Notification
      64              :  */
      65            1 : static inline struct midi_ump make_function_block_info(const struct ump_endpoint_dt_spec *ep,
      66              :                                                        size_t block_num)
      67              : {
      68              :         const struct ump_block_dt_spec *block = &ep->blocks[block_num];
      69              :         struct midi_ump res;
      70              :         uint8_t midi1_mode = block->is_31250bps ? 2 : block->is_midi1 ? 1 : 0;
      71              : 
      72              :         res.data[0] = (UMP_MT_UMP_STREAM << 28)
      73              :                     | (UMP_STREAM_STATUS_FB_INFO << 16)
      74              :                     | BIT(15)  /* Block is active */
      75              :                     | (block_num << 8)
      76              :                     | BIT_IF(block->is_output, 5) /* UI hint Sender */
      77              :                     | BIT_IF(block->is_input, 4)  /* UI hint Receiver */
      78              :                     | (midi1_mode << 2)
      79              :                     | BIT_IF(block->is_output, 1) /* Function block is output */
      80              :                     | BIT_IF(block->is_input, 0); /* Function block is input */
      81              : 
      82              :         res.data[1] = (block->first_group << 24)
      83              :                     | (block->groups_spanned << 16)
      84              :                     | (MIDI_CI_VERSION_FORMAT_UMP_1_1 << 8)  /* MIDI-CI for UMP v1.1 */
      85              :                     | 0xff;  /* At most 255 simultaneous Sysex streams */
      86              : 
      87              :         return res;
      88              : }
      89              : 
      90              : /**
      91              :  * @brief      Copy an ASCII string into a Universal MIDI Packet while leaving
      92              :  *             some most significant bytes untouched, such that the caller can
      93              :  *             set this prefix.
      94              :  * @param      ump     The ump into which the string is copied
      95              :  * @param[in]  offset  Number of bytes from the most-significant side to leave free
      96              :  * @param[in]  src     The source string
      97              :  * @param[in]  len     The length of the source string
      98              :  * @return     The number of bytes copied
      99              :  */
     100            1 : static inline size_t fill_str(struct midi_ump *ump, size_t offset,
     101              :                               const char *src, size_t len)
     102              : {
     103              :         size_t i, j;
     104              : 
     105              :         if (offset >= sizeof(struct midi_ump)) {
     106              :                 return 0;
     107              :         }
     108              : 
     109              :         for (i = 0; i < len && (j = i + offset) < sizeof(struct midi_ump); i++) {
     110              :                 ump->data[j / 4] |= src[i] << (8 * (3 - (j % 4)));
     111              :         }
     112              : 
     113              :         return i;
     114              : }
     115              : 
     116              : /**
     117              :  * @brief  Send a string as UMP Stream, possibly splitting into multiple
     118              :  *         packets if the string length is larger than 1 UMP
     119              :  * @param[in]  cfg     The responder configuration
     120              :  * @param[in]  string  The string to send
     121              :  * @param[in]  prefix  The fixed prefix of UMP packets to send
     122              :  * @param[in]  offset  The offset the strings starts in the packet, in bytes
     123              :  *
     124              :  * @return       The number of packets sent
     125              :  */
     126            1 : static inline int send_string(const struct ump_stream_responder_cfg *cfg,
     127              :                               const char *string, uint32_t prefix, size_t offset)
     128              : {
     129              :         struct midi_ump reply;
     130              :         size_t stringlen = strlen(string);
     131              :         size_t strwidth = sizeof(reply) - offset;
     132              :         uint8_t format;
     133              :         size_t i = 0;
     134              :         int res = 0;
     135              : 
     136              :         while (i < stringlen) {
     137              :                 memset(&reply, 0, sizeof(reply));
     138              :                 format = (i == 0)
     139              :                         ? (stringlen - i <= strwidth)
     140              :                                 ? UMP_STREAM_FORMAT_COMPLETE
     141              :                                 : UMP_STREAM_FORMAT_START
     142              :                         : (stringlen - i > strwidth)
     143              :                                 ? UMP_STREAM_FORMAT_CONTINUE
     144              :                                 : UMP_STREAM_FORMAT_END;
     145              : 
     146              :                 reply.data[0] = (UMP_MT_UMP_STREAM << 28)
     147              :                               | (format << 26)
     148              :                               | prefix;
     149              : 
     150              :                 i += fill_str(&reply, offset, &string[i], stringlen - i);
     151              :                 cfg->send(cfg->dev, reply);
     152              :                 res++;
     153              :         }
     154              : 
     155              :         return res;
     156              : }
     157              : 
     158              : /**
     159              :  * @brief      Handle Endpoint Discovery messages
     160              :  * @param[in]  cfg   The responder configuration
     161              :  * @param[in]  pkt   The discovery packet to handle
     162              :  * @return     The number of UMP sent as reply
     163              :  */
     164            1 : static inline int ump_ep_discover(const struct ump_stream_responder_cfg *cfg,
     165              :                                   const struct midi_ump pkt)
     166              : {
     167              :         int res = 0;
     168              :         uint8_t filter = UMP_STREAM_EP_DISCOVERY_FILTER(pkt);
     169              : 
     170              :         /* Request for Endpoint Info Notification */
     171              :         if ((filter & UMP_EP_DISC_FILTER_EP_INFO) != 0U) {
     172              :                 cfg->send(cfg->dev, make_endpoint_info(cfg->ep_spec));
     173              :                 res++;
     174              :         }
     175              : 
     176              :         /* Request for Endpoint Name Notification */
     177              :         if ((filter & UMP_EP_DISC_FILTER_EP_NAME) != 0U && cfg->ep_spec->name != NULL) {
     178              :                 res += send_string(cfg, cfg->ep_spec->name,
     179              :                                    UMP_STREAM_STATUS_EP_NAME << 16, 2);
     180              :         }
     181              : 
     182              :         /* Request for Product Instance ID */
     183              :         if ((filter & UMP_EP_DISC_FILTER_PRODUCT_ID) != 0U && IS_ENABLED(CONFIG_HWINFO)) {
     184              :                 res += send_string(cfg, ump_product_instance_id(),
     185              :                                    UMP_STREAM_STATUS_PROD_ID << 16, 2);
     186              :         }
     187              : 
     188              :         return res;
     189              : }
     190              : 
     191            0 : static inline int ump_fb_discover_block(const struct ump_stream_responder_cfg *cfg,
     192              :                                         size_t block_num, uint8_t filter)
     193              : {
     194              :         int res = 0;
     195              :         const struct ump_block_dt_spec *blk = &cfg->ep_spec->blocks[block_num];
     196              : 
     197              :         if ((filter & UMP_FB_DISC_FILTER_INFO) != 0U) {
     198              :                 cfg->send(cfg->dev, make_function_block_info(cfg->ep_spec, block_num));
     199              :                 res++;
     200              :         }
     201              : 
     202              :         if ((filter & UMP_FB_DISC_FILTER_NAME) != 0U && blk->name != NULL) {
     203              :                 res += send_string(cfg, blk->name,
     204              :                                    (UMP_STREAM_STATUS_FB_NAME << 16) | (block_num << 8), 3);
     205              :         }
     206              : 
     207              :         return res;
     208              : }
     209              : 
     210              : /**
     211              :  * @brief      Handle Function Block Discovery messages
     212              :  * @param[in]  cfg   The responder configuration
     213              :  * @param[in]  pkt   The discovery packet to handle
     214              :  * @return     The number of UMP sent as reply
     215              :  */
     216            1 : static inline int ump_fb_discover(const struct ump_stream_responder_cfg *cfg,
     217              :                                   const struct midi_ump pkt)
     218              : {
     219              :         int res = 0;
     220              :         uint8_t block_num = UMP_STREAM_FB_DISCOVERY_NUM(pkt);
     221              :         uint8_t filter = UMP_STREAM_FB_DISCOVERY_FILTER(pkt);
     222              : 
     223              :         if (block_num < cfg->ep_spec->n_blocks) {
     224              :                 res += ump_fb_discover_block(cfg, block_num, filter);
     225              :         } else if (block_num == 0xff) {
     226              :                 /* Requesting information for all blocks at once */
     227              :                 for (block_num = 0; block_num < cfg->ep_spec->n_blocks; block_num++) {
     228              :                         res += ump_fb_discover_block(cfg, block_num, filter);
     229              :                 }
     230              :         }
     231              : 
     232              :         return res;
     233              : }
     234              : 
     235              : const char *ump_product_instance_id(void)
     236              : {
     237              :         static char product_id[43] = "";
     238              :         static const char hex[] = "0123456789ABCDEF";
     239              : 
     240              :         if (IS_ENABLED(CONFIG_HWINFO) && product_id[0] == '\0') {
     241              :                 uint8_t devid[sizeof(product_id) / 2];
     242              :                 ssize_t len = hwinfo_get_device_id(devid, sizeof(devid));
     243              : 
     244              :                 if (len == -ENOSYS && hwinfo_get_device_eui64(devid) == 0) {
     245              :                         /* device id unavailable, but there is an eui64,
     246              :                          * which has a fixed length of 8
     247              :                          */
     248              :                         len = 8;
     249              :                 } else if (len < 0) {
     250              :                         /* Other hwinfo driver error; mark as empty */
     251              :                         len = 0;
     252              :                 }
     253              : 
     254              :                 /* Convert to hex string */
     255              :                 for (ssize_t i = 0; i < len; i++) {
     256              :                         product_id[2 * i] = hex[devid[i] >> 4];
     257              :                         product_id[2 * i + 1] = hex[devid[i] & 0xf];
     258              :                 }
     259              :                 product_id[2*len] = '\0';
     260              :         }
     261              : 
     262              :         return product_id;
     263              : }
     264              : 
     265              : int ump_stream_respond(const struct ump_stream_responder_cfg *cfg,
     266              :                        const struct midi_ump pkt)
     267              : {
     268              :         if (cfg->send == NULL) {
     269              :                 return -EINVAL;
     270              :         }
     271              : 
     272              :         if (UMP_MT(pkt) != UMP_MT_UMP_STREAM) {
     273              :                 return 0;
     274              :         }
     275              : 
     276              :         switch (UMP_STREAM_STATUS(pkt)) {
     277              :         case UMP_STREAM_STATUS_EP_DISCOVERY:
     278              :                 return ump_ep_discover(cfg, pkt);
     279              :         case UMP_STREAM_STATUS_FB_DISCOVERY:
     280              :                 return ump_fb_discover(cfg, pkt);
     281              :         }
     282              : 
     283              :         return 0;
     284              : }
        

Generated by: LCOV version 2.0-1