Re: [PATCH v3 06/11] shared/hfp: Add send AT command API for HFP HF

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 8 October 2014 12:15, Lukasz Rymanowski <lukasz.rymanowski@xxxxxxxxx> wrote:
> This patch adds handling send and response of AT command.
> Note that we always wait for AT command response before sending next
> command, however user can fill hfp_hf with more than one command.
> All the commands are queued and send one by one.
> ---
>  src/shared/hfp.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/shared/hfp.h |   5 ++
>  2 files changed, 187 insertions(+)
>
> diff --git a/src/shared/hfp.c b/src/shared/hfp.c
> index d61d76d..9681aae 100644
> --- a/src/shared/hfp.c
> +++ b/src/shared/hfp.c
> @@ -70,6 +70,10 @@ struct hfp_hf {
>         struct ringbuf *read_buf;
>         struct ringbuf *write_buf;
>
> +       bool writer_active;
> +       struct queue *cmd_queue;
> +       bool command_in_progress;
> +
>         struct queue *event_handlers;
>
>         hfp_debug_func_t debug_callback;
> @@ -101,6 +105,14 @@ struct hfp_hf_result {
>         unsigned int offset;
>  };
>
> +struct cmd_response {
> +       char *prefix;
> +       hfp_response_func_t resp_cb;
> +       struct hfp_hf_result *response;
> +       char *resp_data;
> +       void *user_data;
> +};
> +
>  struct event_handler {
>         char *prefix;
>         void *user_data;
> @@ -868,12 +880,74 @@ static void destroy_event_handler(void *data)
>         free(handler);
>  }
>
> +static bool hf_can_write_data(struct io *io, void *user_data)
> +{
> +       struct hfp_hf *hfp = user_data;
> +       ssize_t bytes_written;
> +
> +       bytes_written = ringbuf_write(hfp->write_buf, hfp->fd);
> +       if (bytes_written < 0)
> +               return false;
> +
> +       if (ringbuf_len(hfp->write_buf) > 0)
> +               return true;
> +
> +       return false;
> +}
> +
> +static void hf_write_watch_destroy(void *user_data)
> +{
> +       struct hfp_hf *hfp = user_data;
> +
> +       hfp->writer_active = false;
> +}
> +
>  static void hf_skip_whitespace(struct hfp_hf_result *result)
>  {
>         while (result->data[result->offset] == ' ')
>                 result->offset++;
>  }
>
> +static bool is_response(const char *msg, enum hfp_result *result)
> +{
> +       if (strcmp(msg, "OK") == 0) {
> +               *result = HFP_RESULT_OK;
> +               return true;
> +       }
> +
> +       if (strcmp(msg, "ERROR") == 0) {
> +               *result = HFP_RESULT_ERROR;
> +               return true;
> +       }
> +
> +       return false;
> +}
> +
> +static void hf_wakeup_writer(struct hfp_hf *hfp)
> +{
> +       if (hfp->writer_active)
> +               return;
> +
> +       if (!ringbuf_len(hfp->write_buf))
> +               return;
> +
> +       if (!io_set_write_handler(hfp->io, hf_can_write_data,
> +                                       hfp, hf_write_watch_destroy))
> +               return;
> +
> +       hfp->writer_active = true;
> +}
> +
> +static void destroy_cmd_response(void *data)
> +{
> +       struct cmd_response *cmd = data;
> +
> +       free(cmd->prefix);
> +       free(cmd->resp_data);
> +       free(cmd->response);
> +       free(cmd);
> +}
> +
>  static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data)
>  {
>         struct event_handler *handler;
> @@ -904,6 +978,46 @@ static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data)
>         lookup_prefix[pref_len] = '\0';
>         result_data.offset += pref_len + 1;
>
> +       if (hfp->command_in_progress) {
> +               struct cmd_response *cmd;
> +               enum hfp_result result;
> +
> +               cmd = queue_peek_head(hfp->cmd_queue);
> +               if (!cmd)
> +                       return;
> +
> +               if (is_response(lookup_prefix, &result)) {
> +                       cmd->resp_cb(result, cmd->response, cmd->user_data);
> +
> +                       queue_remove(hfp->cmd_queue, cmd);
> +                       destroy_cmd_response(cmd);
> +
> +                       if (!queue_isempty(hfp->cmd_queue)) {
> +                               hf_wakeup_writer(hfp);
> +                               return;
> +                       }
> +
> +                       hfp->command_in_progress = false;
> +
> +                       return;
> +               }
> +               /*
> +                * Check if unsolicited result is the response for ongoing
> +                * command. If not we try to find registered handler for it
> +                * later.
> +                */
> +               if (strcmp(lookup_prefix, &cmd->prefix[2]) == 0 &&
> +                                                       !cmd->resp_data) {

Looks like I missed the case where we can get more then one response
before OK e.g. on AT+CLCC we can get +CLCC, ..., +CLCC, OK.
Will fix that.
> +                       /* Store response and wait for OK */
> +                       cmd->resp_data = strdup(result_data.data);
> +
> +                       cmd->response = new0(struct hfp_hf_result, 1);
> +                       cmd->response->offset = result_data.offset;
> +                       cmd->response->data = cmd->resp_data;
> +                       return;
> +               }
> +       }
> +
>         handler = queue_find(hfp->event_handlers, match_handler_event_prefix,
>                                                                 lookup_prefix);
>
> @@ -1033,6 +1147,19 @@ struct hfp_hf *hfp_hf_new(int fd)
>                 return NULL;
>         }
>
> +       hfp->cmd_queue = queue_new();
> +       if (!hfp->cmd_queue) {
> +               io_destroy(hfp->io);
> +               ringbuf_free(hfp->write_buf);
> +               ringbuf_free(hfp->read_buf);
> +               queue_destroy(hfp->event_handlers, NULL);
> +               free(hfp);
> +               return NULL;
> +       }
> +
> +       hfp->writer_active = false;
> +       hfp->command_in_progress = false;
> +
>         if (!io_set_read_handler(hfp->io, hf_can_read_data, hfp,
>                                                         read_watch_destroy)) {
>                 queue_destroy(hfp->event_handlers,
> @@ -1084,6 +1211,9 @@ void hfp_hf_unref(struct hfp_hf *hfp)
>         queue_destroy(hfp->event_handlers, destroy_event_handler);
>         hfp->event_handlers = NULL;
>
> +       queue_destroy(hfp->cmd_queue, destroy_cmd_response);
> +       hfp->cmd_queue = NULL;
> +
>         if (!hfp->in_disconnect) {
>                 free(hfp);
>                 return;
> @@ -1143,6 +1273,58 @@ bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close)
>         return true;
>  }
>
> +bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb,
> +                               void *user_data, const char *format, ...)
> +{
> +       va_list ap;
> +       char *fmt;
> +       int len;
> +       const char *separators = ";?=\0";
> +       uint8_t prefix_len;
> +       struct cmd_response *cmd;
> +
> +       if (!hfp || !format || !resp_cb)
> +               return false;
> +
> +       if (asprintf(&fmt, "%s\r", format) < 0)
> +               return false;
> +
> +       cmd = new0(struct cmd_response, 1);
> +       if (!cmd)
> +               return false;
> +
> +       va_start(ap, format);
> +       len = ringbuf_vprintf(hfp->write_buf, fmt, ap);
> +       va_end(ap);
> +
> +       free(fmt);
> +
> +       if (len < 0) {
> +               free(cmd);
> +               return false;
> +       }
> +
> +       prefix_len = strcspn(format, separators);
> +       cmd->prefix = strndup(format, prefix_len);
> +       cmd->resp_cb = resp_cb;
> +       cmd->user_data = user_data;
> +
> +       if (!queue_push_tail(hfp->cmd_queue, cmd)) {
> +               ringbuf_drain(hfp->write_buf, len);
> +               free(cmd);
> +               return false;
> +       }
> +
> +       if (hfp->command_in_progress)
> +               return true;
> +
> +       hfp->command_in_progress = true;
> +
> +       hf_wakeup_writer(hfp);
> +
> +       return true;
> +}
> +
>  bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
>                                                 const char *prefix,
>                                                 void *user_data,
> diff --git a/src/shared/hfp.h b/src/shared/hfp.h
> index 85037b1..773d827 100644
> --- a/src/shared/hfp.h
> +++ b/src/shared/hfp.h
> @@ -83,6 +83,9 @@ typedef void (*hfp_command_func_t)(const char *command, void *user_data);
>
>  typedef void (*hfp_disconnect_func_t)(void *user_data);
>
> +typedef void (*hfp_response_func_t)(enum hfp_result result,
> +                                               struct hfp_hf_result *resp,
> +                                               void *user_data);
>  struct hfp_gw;
>  struct hfp_hf;
>
> @@ -146,3 +149,5 @@ bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
>                                         const char *prefix, void *user_data,
>                                         hfp_destroy_func_t destroy);
>  bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix);
> +bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb,
> +                               void *user_data, const char *format, ...);
> --
> 1.8.4
>

\Łukasz
--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux