/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-11-25 heyuanjie87 the first version */ #include #include #include #include #include struct adb_shdev { struct rt_device parent; struct rt_ringbuffer *rbuf; struct rt_ringbuffer *wbuf; struct rt_event notify; }; #define ADEV_EXIT (0x01) #define ADEV_EREADY (0x10) #define ADEV_READ (0x02) #define ADEV_WRITE (0x04) #define ADEV_WREADY (0x08) struct shell_ext { struct adb_packet *cur; adb_queue_t recv_que; rt_ubase_t rque_buf[4]; struct adb_shdev *dev; rt_thread_t shid; struct rt_event notify; rt_thread_t worker; int old_flag; int mode; }; static struct adb_shdev _shdev; static struct rt_mutex _lock; static int _evwait(struct rt_event *notify, int ev, int ms) { int r = 0; rt_event_recv(notify, ev, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, rt_tick_from_millisecond(ms), (rt_uint32_t*)&r); return r; } static int _adwait(struct adb_shdev *ad, int ev, int ms) { if (!ad->parent.user_data) return ADEV_EXIT; return _evwait(&ad->notify, ev, ms); } static int _adreport(struct adb_shdev *ad, int ev) { if (!ad->parent.user_data) return ADEV_EXIT; rt_event_send(&ad->notify, ev); return 0; } static int _safe_rb_dlen(struct adb_shdev *ad) { int l = 0; if (!ad->parent.user_data) return 0; rt_mutex_take(&_lock, -1); if (ad->wbuf) l = rt_ringbuffer_data_len(ad->wbuf); rt_mutex_release(&_lock); return l; } static int _safe_rb_slen(struct adb_shdev *ad) { int l = 0; if (!ad->parent.user_data) return 0; rt_mutex_take(&_lock, -1); if (ad->wbuf) l = rt_ringbuffer_space_len(ad->wbuf); rt_mutex_release(&_lock); return l; } static bool _safe_rb_read(struct adb_shdev *ad, void *buf, int size) { int l = 0; rt_mutex_take(&_lock, -1); if (ad->wbuf) l = rt_ringbuffer_get(ad->wbuf, (unsigned char *)buf, size); rt_mutex_release(&_lock); return (l != 0); } static int _safe_rb_write(struct adb_shdev *ad, void *buf, int size) { int l = -1; rt_mutex_take(&_lock, -1); if (ad->rbuf) { l = rt_ringbuffer_put(ad->rbuf, (const unsigned char *)buf, size); } rt_mutex_release(&_lock); return l; } static int _safe_rb_ad_read(struct adb_shdev *ad, void *buf, int size) { int l = 0; rt_mutex_take(&_lock, -1); if (ad->rbuf) l = rt_ringbuffer_get(ad->rbuf, (unsigned char *)buf, size); rt_mutex_release(&_lock); return l; } static rt_err_t _shell_service_device_init(struct rt_device *dev) { struct adb_shdev *ad = (struct adb_shdev *)dev; rt_event_init(&ad->notify, "as-sh", 0); ad->rbuf = rt_ringbuffer_create(32); ad->wbuf = rt_ringbuffer_create(256); if (!ad->rbuf || !ad->wbuf) { if (ad->rbuf) rt_ringbuffer_destroy(ad->rbuf); if (ad->wbuf) rt_ringbuffer_destroy(ad->wbuf); rt_event_detach(&ad->notify); return -1; } return 0; } static rt_err_t _shell_service_device_open(struct rt_device *dev, rt_uint16_t oflag) { dev->open_flag = oflag & 0xff; return 0; } static rt_err_t _shell_service_device_close(struct rt_device *dev) { struct adb_shdev *ad = (struct adb_shdev *)dev; _adreport(ad, ADEV_EXIT); _adwait(ad, ADEV_EREADY, 100); rt_mutex_take(&_lock, -1); if (dev->user_data) { struct adb_service *ser; ser = (struct adb_service *)dev->user_data; ser->online = 0; dev->user_data = 0; } rt_ringbuffer_destroy(ad->wbuf); rt_ringbuffer_destroy(ad->rbuf); ad->wbuf = 0; ad->rbuf = 0; rt_mutex_release(&_lock); rt_event_detach(&ad->notify); return 0; } static rt_size_t _shell_service_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) { struct adb_shdev *ad = (struct adb_shdev *)dev; int len = 0; if (!dev->user_data) goto _exit; _retry: len = _safe_rb_ad_read(ad, buffer, size); if (len == 0) { int ret = _adwait(ad, ADEV_READ | ADEV_EXIT, -1); if (ret & ADEV_EXIT) { _adreport(ad, ADEV_EREADY); goto _exit; } if (ret & ADEV_READ) goto _retry; } _exit: if (len == 0) len = -EAGAIN; return len; } static char *crlf_rpc(const char *str, rt_size_t old_size, rt_size_t * new_size) { char * bstr = malloc(old_size * 2); int index = 0; int i; for(i = 0;i < old_size; i++) { if(str[i] == '\n') { bstr[index++] = '\r'; } bstr[index++] = str[i]; } *new_size = index; return bstr; } static rt_size_t _shell_service_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { struct adb_shdev *ad = (struct adb_shdev *)dev; int wlen = 0, r = 0; rt_size_t old_size = size; char *spos = crlf_rpc((const char *)buffer, size, &size); if (!dev->user_data) goto __exit; while (_safe_rb_slen(ad) < size) { r = _adwait(ad, ADEV_WREADY, 20); /* wait event timeout */ if (r == 0) goto __exit; } if (rt_interrupt_get_nest()) { wlen = rt_ringbuffer_put(ad->wbuf, (unsigned char *)spos, size); goto __exit; } rt_mutex_take(&_lock, -1); wlen = rt_ringbuffer_put(ad->wbuf, (unsigned char *)spos, size); rt_mutex_release(&_lock); _adreport(ad, ADEV_WRITE); __exit: free(spos); return (wlen == size ? old_size : wlen); } static rt_err_t _shell_service_device_ctrl(rt_device_t dev, int cmd, void *args) { int ret = 0; return ret; } #ifdef RT_USING_DEVICE_OPS const static struct rt_device_ops shell_ops = { _shell_service_device_init, _shell_service_device_open, _shell_service_device_close, _shell_service_device_read, _shell_service_device_write, _shell_service_device_ctrl }; #endif static int _device_init(rt_device_t shell_device, void *usrdat) { int ret; shell_device->type = RT_Device_Class_Char; #ifdef RT_USING_DEVICE_OPS shell_device->ops = &shell_ops; #else shell_device->init = _shell_service_device_init; shell_device->open = _shell_service_device_open; shell_device->close = _shell_service_device_close; shell_device->read = _shell_service_device_read; shell_device->write = _shell_service_device_write; shell_device->control = _shell_service_device_ctrl; #endif shell_device->user_data = usrdat; ret = rt_device_register(shell_device, "as-sh", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX); return ret; } static bool send_ready(struct shell_ext *ext, char *args) { struct adb_packet *p; ext->mode = rt_strlen(args); p = adb_packet_new(ext->mode + 1); if (!p) return false; p->msg.data_length = ext->mode + 1; rt_memcpy(p->payload, args, ext->mode); p->payload[ext->mode] = '\n'; if (!adb_packet_enqueue(&ext->recv_que, p, 0)) { adb_packet_delete(p); return false; } return true; } extern int libc_stdio_set_console(const char* device_name, int mode); extern int libc_stdio_get_console(void); static int _shell_open(struct adb_service *ser, char *args) { int ret = -1; struct shell_ext *ext; ext = (struct shell_ext *)ser->extptr; #ifdef RT_USING_FINSH ext->shid = rt_thread_find(FINSH_THREAD_NAME); if (!ext->shid) { return -1; } #endif ret = _device_init(&_shdev.parent, ser); if (ret == 0) { int pr = 23; rt_mb_init(&ext->recv_que, "as-sh", ext->rque_buf, sizeof(ext->rque_buf) / sizeof(ext->rque_buf[0]), 0); rt_event_init(&ext->notify, "as-sh", 0); ext->dev = &_shdev; ext->dev->parent.rx_indicate = rt_console_get_device()->rx_indicate; ext->old_flag = ioctl(libc_stdio_get_console(), F_GETFL, 0); ioctl(libc_stdio_get_console(), F_SETFL, (void *)(size_t)(ext->old_flag | O_NONBLOCK)); #ifdef RT_USING_FINSH rt_thread_control(ext->shid, RT_THREAD_CTRL_CHANGE_PRIORITY, (void *)&pr); #endif libc_stdio_set_console("as-sh", O_RDWR); ext->dev->parent.rx_indicate(rt_console_get_device(), 0); rt_console_set_device("as-sh"); #ifdef RT_USING_FINSH rt_thread_resume(ext->shid); #endif rt_thread_mdelay(50); ret = rt_thread_startup(ext->worker); if (ret == 0) { if (!send_ready(ext, args)) { //todo ret = -1; } } } return ret; } static int _shell_close(struct adb_service *ser) { int ret = 0; struct shell_ext *ext; ext = (struct shell_ext *)ser->extptr; ser->online = 0; ext->old_flag = ioctl(libc_stdio_get_console(), F_GETFL, 0); ioctl(libc_stdio_get_console(), F_SETFL, (void *)(size_t)(ext->old_flag | O_NONBLOCK)); rt_console_set_device(RT_CONSOLE_DEVICE_NAME); libc_stdio_set_console(RT_CONSOLE_DEVICE_NAME, O_RDWR); ext->dev->parent.rx_indicate(rt_console_get_device(), 0); rt_console_get_device()->rx_indicate = ext->dev->parent.rx_indicate; rt_thread_resume(ext->shid); rt_thread_mdelay(50); _evwait(&ext->notify, ADEV_EXIT, 100); rt_mb_detach(&ext->recv_que); rt_event_detach(&ext->notify); rt_device_unregister(&ext->dev->parent); return ret; } static bool _shell_enqueue(struct adb_service *ser, struct adb_packet *p, int ms) { struct shell_ext *ext; bool ret; if (!ser->online) return false; ext = (struct shell_ext *)ser->extptr; ret = adb_packet_enqueue(&ext->recv_que, p, ms); return ret; } static const struct adb_service_ops _ops = { _shell_open, _shell_close, _shell_enqueue }; static int do_readdev(struct adb_service *ser, struct shell_ext *ext) { int size; struct adb_packet *p; size = _safe_rb_dlen(ext->dev); if (size == 0) return 0; p = adb_packet_new(size); if (!p) return 0; if (!_safe_rb_read(ext->dev, p->payload, size)) { adb_packet_delete(p); return ADEV_EXIT; } if (!adb_service_sendpacket(ser, p, 60)) { adb_packet_delete(p); } return _adreport(ext->dev, ADEV_WREADY); } static int do_writedev(struct adb_service *ser, struct shell_ext *ext) { struct adb_packet *p; char *pos; int len; if (!ext->cur) { if (!adb_packet_dequeue(&ext->recv_que, &ext->cur, 10)) return 0; ext->cur->split = 0; } p = ext->cur; pos = p->payload + p->split; len = _safe_rb_write(ext->dev, pos, p->msg.data_length); if (len < 0) return ADEV_EXIT; if (len > 0 && ext->dev->parent.rx_indicate != RT_NULL) { ext->dev->parent.rx_indicate(&(ext->dev->parent), len); } p->split += len; p->msg.data_length -= len; if (p->msg.data_length == 0) { ext->cur = 0; adb_packet_delete(p); } return _adreport(ext->dev, ADEV_READ); } static void service_thread(void *arg) { struct adb_service *ser; struct shell_ext *ext; unsigned revt; int exit = 40; ser = arg; ext = ser->extptr; ser->online = 1; while (ser->online) { if (do_writedev(ser, ext) != 0) break; revt = _adwait(ext->dev, ADEV_WRITE | ADEV_EXIT, 20); if (revt & ADEV_EXIT) break; if (revt & ADEV_WRITE) { exit = 10; if (do_readdev(ser, ext) != 0) break; } else if (ext->mode != 0) { if (--exit == 0) break; } } ser->online = 0; rt_mutex_take(&_lock, -1); ext->dev->parent.user_data = 0; rt_mutex_release(&_lock); adb_packet_delete(ext->cur); adb_packet_clear(&ext->recv_que); adb_service_close_report(ser); rt_event_send(&ext->notify, ADEV_EXIT); } static struct adb_service *_shell_create(struct adb_service_handler *h) { struct adb_service *ser; struct shell_ext *ext; if (_shdev.parent.ref_count) return RT_NULL; if (rt_thread_find("as-sh")) return RT_NULL; ser = adb_service_alloc(&_ops, sizeof(struct shell_ext)); if (ser) { ext = (struct shell_ext *)ser->extptr; ext->dev = &_shdev; ext->worker = rt_thread_create("as-sh", service_thread, ser, LPKG_ADB_TR_STACK_SIZE, 22, 20); } return ser; } static void _shell_destroy(struct adb_service_handler *h, struct adb_service *s) { rt_free(s); } int adb_shell_init(void) { static struct adb_service_handler _h; _h.name = "shell:"; _h.create = _shell_create; _h.destroy = _shell_destroy; rt_mutex_init(&_lock, "as-sh", 0); return adb_service_handler_register(&_h); } INIT_APP_EXPORT(adb_shell_init); static void exitas(int argc, char **argv) { rt_thread_t tid; if (_shdev.parent.ref_count == 0) { rt_kprintf("adb shell service not run\n"); return; } rt_mutex_take(&_lock, -1); tid = rt_thread_find("as-sh"); if (tid) { struct adb_service *ser; ser = tid->parameter; //ser->online = 0; _shell_close(ser); } rt_mutex_release(&_lock); } MSH_CMD_EXPORT(exitas, exit adb shell service);