1 /*
2  * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3  */
4 
5 #include <poll.h>
6 
7 #include <aos/driver/wdg.h>
8 #include <vfsdev/wdg_dev.h>
9 #include <devicevfs/devicevfs.h>
10 
11 #ifdef CONFIG_WDG_NUM
12 #define PLATFORM_WDG_NUM CONFIG_WDG_NUM
13 #else
14 #define PLATFORM_WDG_NUM 1
15 #endif
16 
17 #if (PLATFORM_WDG_NUM > 0)
18 
19 // WDG device node will be named with "/dev/wdg<x>", where <x> is wdg port id
20 #define WDG_DEV_NAME_FORMAT "wdg%d"
21 
22 typedef struct vfs_wdg {
23     unsigned char port;   /**< wdg port */
24 } vfs_wdg_t;
25 
wdg_device_ioctl(file_t * f,int cmd,unsigned long arg)26 int wdg_device_ioctl (file_t *f, int cmd, unsigned long arg) {
27     int ret = -1;
28     vfs_wdg_t *vt = (vfs_wdg_t *)f->node->i_arg;
29     wdg_dev_handle_t wdg = (wdg_dev_handle_t)f->f_arg;
30 
31     if (!vt || !wdg) {
32         ddkc_err("%s invalid vt:%p or wdg:%p", vt, wdg);
33         return -EINVAL;
34     }
35 
36     ddkc_dbg("cmd:0x%x, arg:0x%lx\r\n", cmd, arg);
37 
38     switch (cmd) {
39         case IOC_WDG_RELOAD:
40         case IOC_WDG_FEED:
41             ret = aos_wdg_feed(wdg);
42             if (ret) {
43                 ddkc_err("%s feed watchdog failed\r\n", __func__, ret);
44             }
45             break;
46         case IOC_WDG_START:
47             ret = aos_wdg_start(wdg);
48             if (ret) {
49                 ddkc_err("%s start watchdog failed\r\n", __func__, ret);
50             }
51             break;
52         case IOC_WDG_STOP:
53             ret = aos_wdg_stop(wdg);
54             if (ret) {
55                 ddkc_err("%s start watchdog failed\r\n", __func__, ret);
56             }
57             break;
58         default:
59             ddkc_err("%s invalid cmd:%d\r\n", __func__, cmd);
60             break;
61     }
62 
63     return ret;
64 }
65 
wdg_device_open(inode_t * node,file_t * f)66 int wdg_device_open (inode_t *node, file_t *f) {
67     int ret = 0;
68     vfs_wdg_t *vt = (vfs_wdg_t *)f->node->i_arg;
69     wdg_dev_handle_t wdg = NULL;
70 
71     if (!vt) {
72         ddkc_err("%s open failed, invalid i_arg:%p\r\n", node->i_name, vt);
73         return -EINVAL;
74     }
75 
76     wdg = aos_wdg_open(vt->port, 0xFFFFFFFF - 1);
77     if (!wdg) {
78         printf("i2c%d open failed\r\n", vt->port);
79         return -1;
80     }
81 
82     f->f_arg = wdg;
83 
84     ddkc_dbg("%s opened, port:%d\r\n", node->i_name, vt->port);
85 
86     return 0;
87 }
88 
wdg_device_close(file_t * f)89 int wdg_device_close (file_t *f) {
90     int ret = 0;
91     vfs_wdg_t *vt = (vfs_wdg_t *)f->node->i_arg;
92     wdg_dev_handle_t wdg = (wdg_dev_handle_t)f->f_arg;
93 
94     if (!vt || !wdg) {
95         ddkc_err("%s invalid vt:%p or wdg:%p", vt, wdg);
96         return -EINVAL;
97     }
98 
99     ddkc_dbg("close %s, port:%d\r\n", f->node->i_name, vt->port);
100 
101     ret = aos_wdg_close(wdg);
102     if (ret) {
103         ddkc_err("aos_wdg_close on wdg%d failed, ret=%d\r\n", vt->port, ret);
104     }
105     f->f_arg = NULL;
106 
107     return 0;
108 }
109 
110 
111 /************************** device ****************************/
112 
113 
114 subsys_file_ops_t wdg_device_fops = {
115     .open = wdg_device_open,
116     .close = wdg_device_close,
117     .read = NULL,
118     .write = NULL,
119     .ioctl = wdg_device_ioctl,
120     .poll = NULL,
121 };
122 
wdg_device_probe(struct u_platform_device * pdev)123 int wdg_device_probe (struct u_platform_device *pdev) {
124     // make sure 0 is returned if probe operation success
125     // or aos_dev_reg procedure will break and no device node will be registered
126     ddkc_dbg("%s\r\n", __func__);
127     return 0;
128 }
129 
wdg_device_remove(struct u_platform_device * pdev)130 int wdg_device_remove (struct u_platform_device *pdev) {
131     ddkc_dbg("%s\r\n", __func__);
132     return 0;
133 }
134 
wdg_device_shutdown(struct u_platform_device * pdev)135 void wdg_device_shutdown (struct u_platform_device *pdev) {
136     ddkc_dbg("%s\r\n", __func__);
137     return;
138 }
139 
wdg_device_suspend(struct u_platform_device * pdev,u_pm_ops_t state)140 int wdg_device_suspend (struct u_platform_device *pdev, u_pm_ops_t state) {
141     ddkc_dbg("%s\r\n", __func__);
142     return 0;
143 }
144 
wdg_device_resume(struct u_platform_device * pdev)145 int wdg_device_resume (struct u_platform_device *pdev) {
146     ddkc_dbg("%s\r\n", __func__);
147     return 0;
148 }
149 
150 struct subsys_drv wdg_device_drv = {
151     .drv_name = "wdg",
152     .probe = wdg_device_probe,
153     .remove = wdg_device_remove,
154     .shutdown = wdg_device_shutdown,
155     .suspend = wdg_device_suspend,
156     .resume = wdg_device_resume,
157 };
158 
159 struct subsys_dev *g_wdg_device_array[PLATFORM_WDG_NUM];
160 
161 
vfs_wdg_drv_init(void)162 int vfs_wdg_drv_init (void) {
163     int i = 0;
164     int j = 0;
165     int ret = 0;
166     int node_name_len = 0;
167     struct subsys_dev **ppsdev = NULL;
168 
169     ddkc_info("wdg vfs driver init starts\r\n");
170 
171     node_name_len = strlen(WDG_DEV_NAME_FORMAT) + 1;
172     ddkc_dbg("node_name_len:%d\r\n", node_name_len);
173 
174     memset(g_wdg_device_array, 0, sizeof(g_wdg_device_array));
175     ppsdev = g_wdg_device_array;
176 
177     for (i = 0; i < PLATFORM_WDG_NUM; i++) {
178         vfs_wdg_t *vt = malloc(sizeof(vfs_wdg_t));
179 
180         *ppsdev = malloc(sizeof(struct subsys_dev) + node_name_len);
181         if (!(*ppsdev) || !vt) {
182             ddkc_info("malloc failed, *ppsdev:%p, vt:%p\r\n", *ppsdev, vt);
183 
184             if (*ppsdev) {
185                 free(*ppsdev);
186                 *ppsdev = NULL;
187             }
188 
189             if (vt)
190                 free(vt);
191 
192             goto err;
193         }
194 
195         memset(*ppsdev, 0, sizeof(struct subsys_dev) + node_name_len);
196         memset(vt, 0, sizeof(*vt));
197         // vfs_wdg_t's port should be remained during the whole driver life
198         vt->port = i;
199 
200         (*ppsdev)->node_name = (char *)((*ppsdev) + 1);
201         snprintf((*ppsdev)->node_name, node_name_len, WDG_DEV_NAME_FORMAT, i);
202         ddkc_dbg("*ppsdev:%p, node_name:%s, (*ppsdev) + 1:%p, sizeof(struct subsys_dev):%d\r\n",
203                 *ppsdev, (*ppsdev)->node_name, (*ppsdev) + 1, sizeof(struct subsys_dev));
204         (*ppsdev)->permission = 0;
205         // please refer to definitions in enum SUBSYS_BUS_TYPE
206         (*ppsdev)->type = BUS_TYPE_PLATFORM;
207         // user_data will be passed to open operation via node->i_arg
208         (*ppsdev)->user_data = vt;
209 
210         ret = aos_dev_reg(*ppsdev, &wdg_device_fops, &wdg_device_drv);
211         if (ret) {
212             ddkc_err("aos_dev_reg for wdg%d failed, ret:%d\r\n", i, ret);
213 
214             free(vt);
215             free(*ppsdev);
216             *ppsdev = NULL;
217 
218             goto err;
219         }
220 
221         ppsdev++;
222     }
223 
224     ddkc_info("wdg vfs driver init finish, ret:%d\r\n", ret);
225     return 0;
226 
227 err:
228     ppsdev = g_wdg_device_array;
229     for (j = 0; j < i; j++) {
230         // shall uninstall wdg devices who are already registered
231         if (*ppsdev) {
232             aos_dev_unreg(*ppsdev);
233 
234             ddkc_info("free memory for wdg%d\r\n", j);
235 
236             if ((*ppsdev)->user_data)
237                 free((*ppsdev)->user_data);
238 
239             free(*ppsdev);
240             *ppsdev = NULL;
241         }
242         ppsdev++;
243     }
244     ddkc_err("wdg vfs driver init failed, ret:%d\r\n", ret);
245 
246     return ret;
247 }
248 
249 VFS_DRIVER_ENTRY(vfs_wdg_drv_init)
250 
251 #endif
252