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