1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-12-07 Shell init ver.
9 */
10
11 #define DBG_TAG "lwp.tty"
12 #define DBG_LVL DBG_INFO
13 #include <rtdbg.h>
14
15 #include "tty_config.h"
16 #include "tty_internal.h"
17 #include "bsd_porting.h"
18 #include "terminal.h"
19
20 #include <fcntl.h>
21
22 static struct dfs_file_ops ptm_fops;
23
ptm_fops_open(struct dfs_file * file)24 static int ptm_fops_open(struct dfs_file *file)
25 {
26 int rc;
27 rt_uint32_t oflags = file->flags;
28 rt_thread_t cur_thr = rt_thread_self();
29
30 if (file->vnode && file->vnode->data)
31 {
32 /**
33 * Filter out illegal flags
34 */
35 if ((oflags & ~(O_RDWR | O_NOCTTY | O_CLOEXEC | O_LARGEFILE)) == 0)
36 {
37 rc = pts_alloc(FFLAGS(oflags & O_ACCMODE), cur_thr, file);
38
39 /* detached operation from devfs */
40 if (rc == 0)
41 file->vnode->fops = &ptm_fops;
42 }
43 else
44 {
45 rc = -EINVAL;
46 }
47 }
48 else
49 {
50 rc = -EINVAL;
51 }
52
53 return rc;
54 }
55
ptm_fops_close(struct dfs_file * file)56 static int ptm_fops_close(struct dfs_file *file)
57 {
58 int rc;
59 lwp_tty_t tp;
60 rt_device_t device;
61
62 if (file->data)
63 {
64 device = (rt_device_t)file->data;
65 tp = rt_container_of(device, struct lwp_tty, parent);
66 rc = bsd_ptsdev_methods.fo_close(tp, rt_thread_self());
67 }
68 else
69 {
70 rc = -EINVAL;
71 }
72
73 return rc;
74 }
75
ptm_fops_read(struct dfs_file * file,void * buf,size_t count,off_t * pos)76 static ssize_t ptm_fops_read(struct dfs_file *file, void *buf, size_t count,
77 off_t *pos)
78 {
79 ssize_t rc = 0;
80 int error;
81 struct uio uio;
82 struct iovec iov;
83 rt_device_t device;
84 struct lwp_tty *tp;
85 int oflags = file->flags;
86
87 if (file->data)
88 {
89 device = (rt_device_t)file->data;
90 tp = rt_container_of(device, struct lwp_tty, parent);
91
92 /* setup uio parameters */
93 iov.iov_base = (void *)buf;
94 iov.iov_len = count;
95 uio.uio_offset = file->fpos;
96 uio.uio_resid = count;
97 uio.uio_iov = &iov;
98 uio.uio_iovcnt = 1;
99 uio.uio_rw = UIO_READ;
100
101 rc = count;
102 error =
103 bsd_ptsdev_methods.fo_read(tp, &uio, 0, oflags, rt_thread_self());
104 rc -= uio.uio_resid;
105 if (error)
106 {
107 rc = error;
108 }
109
110 /* reset file context */
111 file->fpos = uio.uio_offset;
112 }
113
114 return rc;
115 }
116
ptm_fops_write(struct dfs_file * file,const void * buf,size_t count,off_t * pos)117 static ssize_t ptm_fops_write(struct dfs_file *file, const void *buf,
118 size_t count, off_t *pos)
119 {
120 ssize_t rc = 0;
121 int error;
122 struct uio uio;
123 struct iovec iov;
124 rt_device_t device;
125 struct lwp_tty *tp;
126 int oflags = file->flags;
127
128 if (file->data)
129 {
130 device = (rt_device_t)file->data;
131 tp = rt_container_of(device, struct lwp_tty, parent);
132
133 /* setup uio parameters */
134 iov.iov_base = (void *)buf;
135 iov.iov_len = count;
136 uio.uio_offset = file->fpos;
137 uio.uio_resid = count;
138 uio.uio_iov = &iov;
139 uio.uio_iovcnt = 1;
140 uio.uio_rw = UIO_WRITE;
141
142 rc = count;
143 error =
144 bsd_ptsdev_methods.fo_write(tp, &uio, 0, oflags, rt_thread_self());
145 if (error)
146 {
147 rc = error;
148 }
149 else
150 {
151 rc -= uio.uio_resid;
152 }
153
154 /* reset file context */
155 file->fpos = uio.uio_offset;
156 }
157 return rc;
158 }
159
ptm_fops_ioctl(struct dfs_file * file,int cmd,void * arg)160 static int ptm_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
161 {
162 int rc;
163 lwp_tty_t tp;
164 rt_device_t device;
165 rt_ubase_t cmd_normal = (unsigned int)cmd;
166
167 if (file->data)
168 {
169 device = (rt_device_t)file->data;
170 tp = rt_container_of(device, struct lwp_tty, parent);
171
172 switch (cmd_normal)
173 {
174 case TIOCSPTLCK:
175 {
176 int is_lock;
177 if (lwp_get_from_user(&is_lock, arg, sizeof(int)) != sizeof(int))
178 return -EFAULT;
179 pts_set_lock(tp, is_lock);
180 rc = 0;
181 }
182 break;
183 case TIOCGPTLCK:
184 {
185 int is_lock = pts_is_locked(tp);
186 if (lwp_put_to_user(arg, &is_lock, sizeof(int)) != sizeof(int))
187 return -EFAULT;
188 rc = 0;
189 }
190 break;
191 case TIOCGPKT:
192 {
193 int pktmode = pts_get_pktmode(tp);
194 if (lwp_put_to_user(arg, &pktmode, sizeof(int)) != sizeof(int))
195 return -EFAULT;
196 rc = 0;
197 }
198 break;
199 default:
200 rc = bsd_ptsdev_methods.fo_ioctl(
201 tp, cmd_normal, arg, 0, FFLAGS(file->flags), rt_thread_self());
202 break;
203 }
204 }
205 else
206 {
207 rc = -EINVAL;
208 }
209
210 return rc;
211 }
212
ptm_fops_flush(struct dfs_file * file)213 static int ptm_fops_flush(struct dfs_file *file)
214 {
215 return -EINVAL;
216 }
217
ptm_fops_lseek(struct dfs_file * file,off_t offset,int wherece)218 static off_t ptm_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
219 {
220 return -EINVAL;
221 }
222
ptm_fops_truncate(struct dfs_file * file,off_t offset)223 static int ptm_fops_truncate(struct dfs_file *file, off_t offset)
224 {
225 return -EINVAL;
226 }
227
ptm_fops_poll(struct dfs_file * file,struct rt_pollreq * req)228 static int ptm_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
229 {
230 int rc;
231 rt_device_t device;
232 struct lwp_tty *tp;
233
234 if (file->data)
235 {
236 device = (rt_device_t)file->data;
237 tp = rt_container_of(device, struct lwp_tty, parent);
238
239 rc = bsd_ptsdev_methods.fo_poll(tp, req, 0, rt_thread_self());
240 }
241 else
242 {
243 rc = -1;
244 }
245
246 return rc;
247 }
248
ptm_fops_mmap(struct dfs_file * file,struct lwp_avl_struct * mmap)249 static int ptm_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
250 {
251 return -EINVAL;
252 }
253
ptm_fops_lock(struct dfs_file * file,struct file_lock * flock)254 static int ptm_fops_lock(struct dfs_file *file, struct file_lock *flock)
255 {
256 return -EINVAL;
257 }
258
ptm_fops_flock(struct dfs_file * file,int operation,struct file_lock * flock)259 static int ptm_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
260 {
261 return -EINVAL;
262 }
263
264 static struct dfs_file_ops ptm_fops = {
265 .open = ptm_fops_open,
266 .close = ptm_fops_close,
267 .ioctl = ptm_fops_ioctl,
268 .read = ptm_fops_read,
269 .write = ptm_fops_write,
270 .flush = ptm_fops_flush,
271 .lseek = ptm_fops_lseek,
272 .truncate = ptm_fops_truncate,
273 .poll = ptm_fops_poll,
274 .mmap = ptm_fops_mmap,
275 .lock = ptm_fops_lock,
276 .flock = ptm_fops_flock,
277 };
278
lwp_ptmx_init(rt_device_t ptmx_device,const char * root_path)279 rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path)
280 {
281 char *device_name;
282 int root_len;
283 const char *dev_rel_path;
284 rt_err_t rc;
285
286 root_len = strlen(root_path);
287 dev_rel_path = "/ptmx";
288 device_name = rt_malloc(root_len + sizeof("/ptmx"));
289
290 if (device_name)
291 {
292 /* Register device */
293 sprintf(device_name, "%s%s", root_path, dev_rel_path);
294 rt_device_register(ptmx_device, device_name, 0);
295
296 /* Setup fops */
297 ptmx_device->fops = &ptm_fops;
298
299 rt_free(device_name);
300
301 rc = RT_EOK;
302 }
303 else
304 {
305 rc = -RT_ENOMEM;
306 }
307
308 return rc;
309 }
310
311 /* system level ptmx */
312 static struct rt_device sysptmx;
313
314 static struct dfs_file_ops sysptmx_file_ops;
315
sysptmx_readlink(struct rt_device * dev,char * buf,int len)316 static rt_err_t sysptmx_readlink(struct rt_device *dev, char *buf, int len)
317 {
318 int rc = 0;
319
320 /* TODO: support multi-root ? */
321 strncpy(buf, "pts/ptmx", len);
322
323 return rc;
324 }
325
_sys_ptmx_init(void)326 static int _sys_ptmx_init(void)
327 {
328 rt_err_t rc;
329 rt_device_t sysptmx_rtdev = &sysptmx;
330
331 /* setup system level device */
332 sysptmx_rtdev->type = RT_Device_Class_Char;
333 sysptmx_rtdev->ops = RT_NULL;
334 rc = rt_device_register(sysptmx_rtdev, "ptmx", RT_DEVICE_FLAG_DYNAMIC);
335 if (rc == RT_EOK)
336 {
337 sysptmx_rtdev->readlink = &sysptmx_readlink;
338 sysptmx_rtdev->fops = &sysptmx_file_ops;
339 }
340 return rc;
341 }
342 INIT_DEVICE_EXPORT(_sys_ptmx_init);
343