/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2019-05-08 chenyong first version */ #include #include #include "cpuport.h" #include #include #define DBG_TAG "at.dev" #define DBG_LVL DBG_INFO #include /* The global list of at device */ static rt_slist_t at_device_list = RT_SLIST_OBJECT_INIT(at_device_list); /* The global list of at device class */ static rt_slist_t at_device_class_list = RT_SLIST_OBJECT_INIT(at_device_class_list); /** * This function will get the first initialized AT device. * * @return the AT device structure pointer */ struct at_device *at_device_get_first_initialized(void) { rt_base_t level; rt_slist_t *node = RT_NULL; struct at_device *device = RT_NULL; level = rt_hw_interrupt_disable(); rt_slist_for_each(node, &at_device_list) { device = rt_slist_entry(node, struct at_device, list); if (device && device->is_init == RT_TRUE) { rt_hw_interrupt_enable(level); return device; } } rt_hw_interrupt_enable(level); return RT_NULL; } /** * This function will get AT device by device name. * * @param type the name type * @param name the device name or the client name * * @return the AT device structure pointer */ struct at_device *at_device_get_by_name(int type, const char *name) { rt_base_t level; rt_slist_t *node = RT_NULL; struct at_device *device = RT_NULL; RT_ASSERT(name); level = rt_hw_interrupt_disable(); rt_slist_for_each(node, &at_device_list) { device = rt_slist_entry(node, struct at_device, list); if (device) { if (((type == AT_DEVICE_NAMETYPE_DEVICE) || (type == AT_DEVICE_NAMETYPE_NETDEV)) && (rt_strncmp(device->name, name, rt_strlen(name)) == 0)) { rt_hw_interrupt_enable(level); return device; } else if ((type == AT_DEVICE_NAMETYPE_CLIENT) && (rt_strncmp(device->client->device->parent.name, name, rt_strlen(name)) == 0)) { rt_hw_interrupt_enable(level); return device; } } } rt_hw_interrupt_enable(level); return RT_NULL; } #ifdef AT_USING_SOCKET /** * This function will get AT device by ip address. * * @param ip_addr input ip address * network * @return != NULL: network interface device object * NULL: get failed */ struct at_device *at_device_get_by_ipaddr(ip_addr_t *ip_addr) { rt_base_t level; rt_slist_t *node = RT_NULL; struct at_device *device = RT_NULL; level = rt_hw_interrupt_disable(); rt_slist_for_each(node, &at_device_list) { device = rt_slist_entry(node, struct at_device, list); if (device && ip_addr_cmp(ip_addr, &(device->netdev->ip_addr))) { rt_hw_interrupt_enable(level); return device; } } rt_hw_interrupt_enable(level); return RT_NULL; } #endif /* AT_USING_SOCKET */ /** * This function will perform a variety of control functions on AT devices. * * @param device the pointer of AT device structure * @param cmd the command sent to AT device * @param arg the argument of command * * @return = 0: perform successfully * < 0: perform failed */ int at_device_control(struct at_device *device, int cmd, void *arg) { if (device->class->device_ops->control) { return device->class->device_ops->control(device, cmd, arg); } else { LOG_W("AT device(%s) not support control operations.", device->name); return RT_EOK; } } /** * This function registers an AT device class with specified device class ID. * * @param class the pointer of AT device class structure * @param class_id AT device class ID * * @return 0: register successfully */ int at_device_class_register(struct at_device_class *class, uint16_t class_id) { rt_base_t level; RT_ASSERT(class); /* Fill AT device class */ class->class_id = class_id; /* Initialize current AT device class single list */ rt_slist_init(&(class->list)); level = rt_hw_interrupt_disable(); /* Add current AT device class to list */ rt_slist_append(&at_device_class_list, &(class->list)); rt_hw_interrupt_enable(level); return RT_EOK; } /* Get AT device class by client ID */ static struct at_device_class *at_device_class_get(uint16_t class_id) { rt_base_t level; rt_slist_t *node = RT_NULL; struct at_device_class *class = RT_NULL; level = rt_hw_interrupt_disable(); /* Get AT device class by class ID */ rt_slist_for_each(node, &at_device_class_list) { class = rt_slist_entry(node, struct at_device_class, list); if (class && class->class_id == class_id) { rt_hw_interrupt_enable(level); return class; } } rt_hw_interrupt_enable(level); return RT_NULL; } /** * This function registers an AT device with specified device name and AT client name. * * @param device the pointer of AT device structure * @param device_name AT device name * @param at_client_name AT device client name * @param class_id AT device class ID * @param user_data user-specific data * * @return = 0: register successfully * < 0: register failed */ int at_device_register(struct at_device *device, const char *device_name, const char *at_client_name, uint16_t class_id, void *user_data) { rt_base_t level; int result = 0; #ifdef AT_USING_SOCKET static int device_counts = 0; char name[RT_NAME_MAX] = {0}; #endif struct at_device_class *class = RT_NULL; RT_ASSERT(device); RT_ASSERT(device_name); RT_ASSERT(at_client_name); class = at_device_class_get(class_id); if (class == RT_NULL) { LOG_E("get AT device class(%d) failed.", class_id); result = -RT_ERROR; goto __exit; } /* Fill AT device object*/ #ifdef AT_USING_SOCKET device->sockets = (struct at_socket *) rt_calloc(class->socket_num, sizeof(struct at_socket)); if (device->sockets == RT_NULL) { LOG_E("no memory for AT Socket number(%d) create.", class->socket_num); result = -RT_ENOMEM; goto __exit; } /* create AT device socket event */ rt_snprintf(name, RT_NAME_MAX, "at_se%d", device_counts++); device->socket_event = rt_event_create(name, RT_IPC_FLAG_FIFO); if (device->socket_event == RT_NULL) { LOG_E("no memory for AT device(%s) socket event create.", device_name); result = -RT_ENOMEM; goto __exit; } #endif /* AT_USING_SOCKET */ rt_memcpy(device->name, device_name, rt_strlen(device_name)); device->class = class; device->user_data = user_data; /* Initialize current AT device single list */ rt_slist_init(&(device->list)); level = rt_hw_interrupt_disable(); /* Add current AT device to device list */ rt_slist_append(&at_device_list, &(device->list)); rt_hw_interrupt_enable(level); /* Initialize AT device */ result = class->device_ops->init(device); if (result < 0) { goto __exit; } __exit: if (result < 0) { device->is_init = RT_FALSE; } else { device->is_init = RT_TRUE; } return result; }