| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <string.h> |
| #include "os/mynewt.h" |
| |
| static STAILQ_HEAD(, os_dev) g_os_dev_list; |
| |
| static int |
| os_dev_init(struct os_dev *dev, char *name, uint8_t stage, |
| uint8_t priority, os_dev_init_func_t od_init, void *arg) |
| { |
| dev->od_name = name; |
| dev->od_stage = stage; |
| dev->od_priority = priority; |
| /* assume these are set after the fact. */ |
| dev->od_flags = 0; |
| dev->od_open_ref = 0; |
| dev->od_init = od_init; |
| dev->od_init_arg = arg; |
| memset(&dev->od_handlers, 0, sizeof(dev->od_handlers)); |
| |
| return (0); |
| } |
| |
| /** |
| * Add the device to the device tree. This is a private function. |
| * |
| * @param dev The device to add to the device tree. |
| * |
| * @return 0 on success, non-zero on failure. |
| */ |
| static int |
| os_dev_add(struct os_dev *dev) |
| { |
| struct os_dev *cur_dev; |
| |
| /* If no devices present, insert into head */ |
| if (STAILQ_FIRST(&g_os_dev_list) == NULL) { |
| STAILQ_INSERT_HEAD(&g_os_dev_list, dev, od_next); |
| return (0); |
| } |
| |
| /* Add devices to the list, sorted first by stage, then by |
| * priority. Keep sorted in this order for initialization |
| * stage. |
| */ |
| cur_dev = NULL; |
| STAILQ_FOREACH(cur_dev, &g_os_dev_list, od_next) { |
| if (cur_dev->od_stage > dev->od_stage) { |
| continue; |
| } |
| |
| if (dev->od_priority >= cur_dev->od_priority) { |
| break; |
| } |
| } |
| |
| if (cur_dev) { |
| STAILQ_INSERT_AFTER(&g_os_dev_list, cur_dev, dev, od_next); |
| } else { |
| STAILQ_INSERT_TAIL(&g_os_dev_list, dev, od_next); |
| } |
| |
| return (0); |
| } |
| |
| /** |
| * Call device initialize routine, and mark it ready. This is a private |
| * function. |
| * |
| * @param dev The device to initialize. |
| * |
| * @return 0 on success, non-zero on failure. |
| */ |
| static int |
| os_dev_initialize(struct os_dev *dev) |
| { |
| int rc; |
| |
| rc = dev->od_init(dev, dev->od_init_arg); |
| if (rc != 0) { |
| if (dev->od_flags & OS_DEV_F_INIT_CRITICAL) { |
| goto err; |
| } |
| } else { |
| dev->od_flags |= OS_DEV_F_STATUS_READY; |
| } |
| return 0; |
| err: |
| return rc; |
| } |
| |
| int |
| os_dev_create(struct os_dev *dev, char *name, uint8_t stage, |
| uint8_t priority, os_dev_init_func_t od_init, void *arg) |
| { |
| int rc; |
| |
| rc = os_dev_init(dev, name, stage, priority, od_init, arg); |
| if (rc != 0) { |
| goto err; |
| } |
| |
| rc = os_dev_add(dev); |
| if (rc != 0) { |
| goto err; |
| } |
| |
| if (g_os_started) { |
| rc = os_dev_initialize(dev); |
| } |
| err: |
| return (rc); |
| } |
| |
| int |
| os_dev_initialize_all(uint8_t stage) |
| { |
| struct os_dev *dev; |
| int rc = 0; |
| |
| STAILQ_FOREACH(dev, &g_os_dev_list, od_next) { |
| if (dev->od_stage == stage) { |
| rc = os_dev_initialize(dev); |
| if (rc) { |
| break; |
| } |
| } |
| } |
| |
| return (rc); |
| } |
| |
| int |
| os_dev_suspend_all(os_time_t suspend_t, uint8_t force) |
| { |
| struct os_dev *dev; |
| int suspend_failure; |
| int rc; |
| |
| suspend_failure = 0; |
| STAILQ_FOREACH(dev, &g_os_dev_list, od_next) { |
| rc = os_dev_suspend(dev, suspend_t, force); |
| if (rc != 0) { |
| suspend_failure = OS_ERROR; |
| } |
| } |
| |
| return (suspend_failure); |
| } |
| |
| int |
| os_dev_resume_all(void) |
| { |
| struct os_dev *dev; |
| int rc; |
| |
| STAILQ_FOREACH(dev, &g_os_dev_list, od_next) { |
| rc = os_dev_resume(dev); |
| if (rc != 0) { |
| goto err; |
| } |
| } |
| |
| return (0); |
| err: |
| return (rc); |
| } |
| |
| struct os_dev * |
| os_dev_lookup(char *name) |
| { |
| struct os_dev *dev; |
| |
| dev = NULL; |
| STAILQ_FOREACH(dev, &g_os_dev_list, od_next) { |
| if (!strcmp(dev->od_name, name)) { |
| break; |
| } |
| } |
| return (dev); |
| } |
| |
| struct os_dev * |
| os_dev_open(char *devname, uint32_t timo, void *arg) |
| { |
| struct os_dev *dev; |
| os_sr_t sr; |
| int rc; |
| |
| dev = os_dev_lookup(devname); |
| if (dev == NULL) { |
| return (NULL); |
| } |
| |
| /* Device is not ready to be opened. */ |
| if ((dev->od_flags & OS_DEV_F_STATUS_READY) == 0) { |
| return (NULL); |
| } |
| |
| if (dev->od_handlers.od_open) { |
| rc = dev->od_handlers.od_open(dev, timo, arg); |
| if (rc != 0) { |
| goto err; |
| } |
| } |
| |
| OS_ENTER_CRITICAL(sr); |
| ++dev->od_open_ref; |
| dev->od_flags |= OS_DEV_F_STATUS_OPEN; |
| OS_EXIT_CRITICAL(sr); |
| |
| return (dev); |
| err: |
| return (NULL); |
| } |
| |
| int |
| os_dev_close(struct os_dev *dev) |
| { |
| int rc; |
| os_sr_t sr; |
| |
| if (dev->od_handlers.od_close) { |
| rc = dev->od_handlers.od_close(dev); |
| if (rc != 0) { |
| goto err; |
| } |
| } |
| |
| OS_ENTER_CRITICAL(sr); |
| if (--dev->od_open_ref == 0) { |
| dev->od_flags &= ~(OS_DEV_F_STATUS_OPEN | OS_DEV_F_STATUS_SUSPENDED); |
| } |
| OS_EXIT_CRITICAL(sr); |
| |
| return (0); |
| err: |
| return (rc); |
| } |
| |
| int |
| os_dev_suspend(struct os_dev *dev, os_time_t suspend_t, uint8_t force) |
| { |
| os_sr_t sr; |
| int rc; |
| |
| OS_ENTER_CRITICAL(sr); |
| if (!(dev->od_flags & OS_DEV_F_STATUS_OPEN)) { |
| OS_EXIT_CRITICAL(sr); |
| return OS_EINVAL; |
| } |
| if (dev->od_flags & OS_DEV_F_STATUS_SUSPENDED) { |
| OS_EXIT_CRITICAL(sr); |
| return OS_OK; |
| } |
| OS_EXIT_CRITICAL(sr); |
| |
| if (dev->od_handlers.od_suspend) { |
| rc = dev->od_handlers.od_suspend(dev, suspend_t, force); |
| if (rc) { |
| return rc; |
| } |
| } |
| |
| OS_ENTER_CRITICAL(sr); |
| dev->od_flags |= OS_DEV_F_STATUS_SUSPENDED; |
| OS_EXIT_CRITICAL(sr); |
| |
| return OS_OK; |
| } |
| |
| int |
| os_dev_resume(struct os_dev *dev) |
| { |
| os_sr_t sr; |
| int rc; |
| |
| OS_ENTER_CRITICAL(sr); |
| if (!(dev->od_flags & OS_DEV_F_STATUS_SUSPENDED)) { |
| OS_EXIT_CRITICAL(sr); |
| return OS_EINVAL; |
| } |
| OS_EXIT_CRITICAL(sr); |
| |
| if (dev->od_handlers.od_resume) { |
| rc = dev->od_handlers.od_resume(dev); |
| if (rc) { |
| return rc; |
| } |
| } |
| |
| OS_ENTER_CRITICAL(sr); |
| dev->od_flags &= ~OS_DEV_F_STATUS_SUSPENDED; |
| OS_EXIT_CRITICAL(sr); |
| |
| return OS_OK; |
| } |
| |
| void |
| os_dev_reset(void) |
| { |
| STAILQ_INIT(&g_os_dev_list); |
| } |
| |