root/drivers/ptp/ptp_chardev.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ptp_disable_pinfunc
  2. ptp_set_pinfunc
  3. ptp_open
  4. ptp_ioctl
  5. ptp_poll

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * PTP 1588 clock support - character device implementation.
   4  *
   5  * Copyright (C) 2010 OMICRON electronics GmbH
   6  */
   7 #include <linux/module.h>
   8 #include <linux/posix-clock.h>
   9 #include <linux/poll.h>
  10 #include <linux/sched.h>
  11 #include <linux/slab.h>
  12 #include <linux/timekeeping.h>
  13 
  14 #include <linux/nospec.h>
  15 
  16 #include "ptp_private.h"
  17 
  18 static int ptp_disable_pinfunc(struct ptp_clock_info *ops,
  19                                enum ptp_pin_function func, unsigned int chan)
  20 {
  21         struct ptp_clock_request rq;
  22         int err = 0;
  23 
  24         memset(&rq, 0, sizeof(rq));
  25 
  26         switch (func) {
  27         case PTP_PF_NONE:
  28                 break;
  29         case PTP_PF_EXTTS:
  30                 rq.type = PTP_CLK_REQ_EXTTS;
  31                 rq.extts.index = chan;
  32                 err = ops->enable(ops, &rq, 0);
  33                 break;
  34         case PTP_PF_PEROUT:
  35                 rq.type = PTP_CLK_REQ_PEROUT;
  36                 rq.perout.index = chan;
  37                 err = ops->enable(ops, &rq, 0);
  38                 break;
  39         case PTP_PF_PHYSYNC:
  40                 break;
  41         default:
  42                 return -EINVAL;
  43         }
  44 
  45         return err;
  46 }
  47 
  48 int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
  49                     enum ptp_pin_function func, unsigned int chan)
  50 {
  51         struct ptp_clock_info *info = ptp->info;
  52         struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin];
  53         unsigned int i;
  54 
  55         /* Check to see if any other pin previously had this function. */
  56         for (i = 0; i < info->n_pins; i++) {
  57                 if (info->pin_config[i].func == func &&
  58                     info->pin_config[i].chan == chan) {
  59                         pin1 = &info->pin_config[i];
  60                         break;
  61                 }
  62         }
  63         if (pin1 && i == pin)
  64                 return 0;
  65 
  66         /* Check the desired function and channel. */
  67         switch (func) {
  68         case PTP_PF_NONE:
  69                 break;
  70         case PTP_PF_EXTTS:
  71                 if (chan >= info->n_ext_ts)
  72                         return -EINVAL;
  73                 break;
  74         case PTP_PF_PEROUT:
  75                 if (chan >= info->n_per_out)
  76                         return -EINVAL;
  77                 break;
  78         case PTP_PF_PHYSYNC:
  79                 if (chan != 0)
  80                         return -EINVAL;
  81                 break;
  82         default:
  83                 return -EINVAL;
  84         }
  85 
  86         if (info->verify(info, pin, func, chan)) {
  87                 pr_err("driver cannot use function %u on pin %u\n", func, chan);
  88                 return -EOPNOTSUPP;
  89         }
  90 
  91         /* Disable whatever function was previously assigned. */
  92         if (pin1) {
  93                 ptp_disable_pinfunc(info, func, chan);
  94                 pin1->func = PTP_PF_NONE;
  95                 pin1->chan = 0;
  96         }
  97         ptp_disable_pinfunc(info, pin2->func, pin2->chan);
  98         pin2->func = func;
  99         pin2->chan = chan;
 100 
 101         return 0;
 102 }
 103 
 104 int ptp_open(struct posix_clock *pc, fmode_t fmode)
 105 {
 106         return 0;
 107 }
 108 
 109 long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
 110 {
 111         struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 112         struct ptp_sys_offset_extended *extoff = NULL;
 113         struct ptp_sys_offset_precise precise_offset;
 114         struct system_device_crosststamp xtstamp;
 115         struct ptp_clock_info *ops = ptp->info;
 116         struct ptp_sys_offset *sysoff = NULL;
 117         struct ptp_system_timestamp sts;
 118         struct ptp_clock_request req;
 119         struct ptp_clock_caps caps;
 120         struct ptp_clock_time *pct;
 121         unsigned int i, pin_index;
 122         struct ptp_pin_desc pd;
 123         struct timespec64 ts;
 124         int enable, err = 0;
 125 
 126         switch (cmd) {
 127 
 128         case PTP_CLOCK_GETCAPS:
 129         case PTP_CLOCK_GETCAPS2:
 130                 memset(&caps, 0, sizeof(caps));
 131 
 132                 caps.max_adj = ptp->info->max_adj;
 133                 caps.n_alarm = ptp->info->n_alarm;
 134                 caps.n_ext_ts = ptp->info->n_ext_ts;
 135                 caps.n_per_out = ptp->info->n_per_out;
 136                 caps.pps = ptp->info->pps;
 137                 caps.n_pins = ptp->info->n_pins;
 138                 caps.cross_timestamping = ptp->info->getcrosststamp != NULL;
 139                 if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
 140                         err = -EFAULT;
 141                 break;
 142 
 143         case PTP_EXTTS_REQUEST:
 144         case PTP_EXTTS_REQUEST2:
 145                 memset(&req, 0, sizeof(req));
 146 
 147                 if (copy_from_user(&req.extts, (void __user *)arg,
 148                                    sizeof(req.extts))) {
 149                         err = -EFAULT;
 150                         break;
 151                 }
 152                 if (cmd == PTP_EXTTS_REQUEST2) {
 153                         /* Tell the drivers to check the flags carefully. */
 154                         req.extts.flags |= PTP_STRICT_FLAGS;
 155                         /* Make sure no reserved bit is set. */
 156                         if ((req.extts.flags & ~PTP_EXTTS_VALID_FLAGS) ||
 157                             req.extts.rsv[0] || req.extts.rsv[1]) {
 158                                 err = -EINVAL;
 159                                 break;
 160                         }
 161                         /* Ensure one of the rising/falling edge bits is set. */
 162                         if ((req.extts.flags & PTP_ENABLE_FEATURE) &&
 163                             (req.extts.flags & PTP_EXTTS_EDGES) == 0) {
 164                                 err = -EINVAL;
 165                                 break;
 166                         }
 167                 } else if (cmd == PTP_EXTTS_REQUEST) {
 168                         req.extts.flags &= PTP_EXTTS_V1_VALID_FLAGS;
 169                         req.extts.rsv[0] = 0;
 170                         req.extts.rsv[1] = 0;
 171                 }
 172                 if (req.extts.index >= ops->n_ext_ts) {
 173                         err = -EINVAL;
 174                         break;
 175                 }
 176                 req.type = PTP_CLK_REQ_EXTTS;
 177                 enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
 178                 err = ops->enable(ops, &req, enable);
 179                 break;
 180 
 181         case PTP_PEROUT_REQUEST:
 182         case PTP_PEROUT_REQUEST2:
 183                 memset(&req, 0, sizeof(req));
 184 
 185                 if (copy_from_user(&req.perout, (void __user *)arg,
 186                                    sizeof(req.perout))) {
 187                         err = -EFAULT;
 188                         break;
 189                 }
 190                 if (((req.perout.flags & ~PTP_PEROUT_VALID_FLAGS) ||
 191                         req.perout.rsv[0] || req.perout.rsv[1] ||
 192                         req.perout.rsv[2] || req.perout.rsv[3]) &&
 193                         cmd == PTP_PEROUT_REQUEST2) {
 194                         err = -EINVAL;
 195                         break;
 196                 } else if (cmd == PTP_PEROUT_REQUEST) {
 197                         req.perout.flags &= PTP_PEROUT_V1_VALID_FLAGS;
 198                         req.perout.rsv[0] = 0;
 199                         req.perout.rsv[1] = 0;
 200                         req.perout.rsv[2] = 0;
 201                         req.perout.rsv[3] = 0;
 202                 }
 203                 if (req.perout.index >= ops->n_per_out) {
 204                         err = -EINVAL;
 205                         break;
 206                 }
 207                 req.type = PTP_CLK_REQ_PEROUT;
 208                 enable = req.perout.period.sec || req.perout.period.nsec;
 209                 err = ops->enable(ops, &req, enable);
 210                 break;
 211 
 212         case PTP_ENABLE_PPS:
 213         case PTP_ENABLE_PPS2:
 214                 memset(&req, 0, sizeof(req));
 215 
 216                 if (!capable(CAP_SYS_TIME))
 217                         return -EPERM;
 218                 req.type = PTP_CLK_REQ_PPS;
 219                 enable = arg ? 1 : 0;
 220                 err = ops->enable(ops, &req, enable);
 221                 break;
 222 
 223         case PTP_SYS_OFFSET_PRECISE:
 224         case PTP_SYS_OFFSET_PRECISE2:
 225                 if (!ptp->info->getcrosststamp) {
 226                         err = -EOPNOTSUPP;
 227                         break;
 228                 }
 229                 err = ptp->info->getcrosststamp(ptp->info, &xtstamp);
 230                 if (err)
 231                         break;
 232 
 233                 memset(&precise_offset, 0, sizeof(precise_offset));
 234                 ts = ktime_to_timespec64(xtstamp.device);
 235                 precise_offset.device.sec = ts.tv_sec;
 236                 precise_offset.device.nsec = ts.tv_nsec;
 237                 ts = ktime_to_timespec64(xtstamp.sys_realtime);
 238                 precise_offset.sys_realtime.sec = ts.tv_sec;
 239                 precise_offset.sys_realtime.nsec = ts.tv_nsec;
 240                 ts = ktime_to_timespec64(xtstamp.sys_monoraw);
 241                 precise_offset.sys_monoraw.sec = ts.tv_sec;
 242                 precise_offset.sys_monoraw.nsec = ts.tv_nsec;
 243                 if (copy_to_user((void __user *)arg, &precise_offset,
 244                                  sizeof(precise_offset)))
 245                         err = -EFAULT;
 246                 break;
 247 
 248         case PTP_SYS_OFFSET_EXTENDED:
 249         case PTP_SYS_OFFSET_EXTENDED2:
 250                 if (!ptp->info->gettimex64) {
 251                         err = -EOPNOTSUPP;
 252                         break;
 253                 }
 254                 extoff = memdup_user((void __user *)arg, sizeof(*extoff));
 255                 if (IS_ERR(extoff)) {
 256                         err = PTR_ERR(extoff);
 257                         extoff = NULL;
 258                         break;
 259                 }
 260                 if (extoff->n_samples > PTP_MAX_SAMPLES
 261                     || extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2]) {
 262                         err = -EINVAL;
 263                         break;
 264                 }
 265                 for (i = 0; i < extoff->n_samples; i++) {
 266                         err = ptp->info->gettimex64(ptp->info, &ts, &sts);
 267                         if (err)
 268                                 goto out;
 269                         extoff->ts[i][0].sec = sts.pre_ts.tv_sec;
 270                         extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec;
 271                         extoff->ts[i][1].sec = ts.tv_sec;
 272                         extoff->ts[i][1].nsec = ts.tv_nsec;
 273                         extoff->ts[i][2].sec = sts.post_ts.tv_sec;
 274                         extoff->ts[i][2].nsec = sts.post_ts.tv_nsec;
 275                 }
 276                 if (copy_to_user((void __user *)arg, extoff, sizeof(*extoff)))
 277                         err = -EFAULT;
 278                 break;
 279 
 280         case PTP_SYS_OFFSET:
 281         case PTP_SYS_OFFSET2:
 282                 sysoff = memdup_user((void __user *)arg, sizeof(*sysoff));
 283                 if (IS_ERR(sysoff)) {
 284                         err = PTR_ERR(sysoff);
 285                         sysoff = NULL;
 286                         break;
 287                 }
 288                 if (sysoff->n_samples > PTP_MAX_SAMPLES) {
 289                         err = -EINVAL;
 290                         break;
 291                 }
 292                 pct = &sysoff->ts[0];
 293                 for (i = 0; i < sysoff->n_samples; i++) {
 294                         ktime_get_real_ts64(&ts);
 295                         pct->sec = ts.tv_sec;
 296                         pct->nsec = ts.tv_nsec;
 297                         pct++;
 298                         if (ops->gettimex64)
 299                                 err = ops->gettimex64(ops, &ts, NULL);
 300                         else
 301                                 err = ops->gettime64(ops, &ts);
 302                         if (err)
 303                                 goto out;
 304                         pct->sec = ts.tv_sec;
 305                         pct->nsec = ts.tv_nsec;
 306                         pct++;
 307                 }
 308                 ktime_get_real_ts64(&ts);
 309                 pct->sec = ts.tv_sec;
 310                 pct->nsec = ts.tv_nsec;
 311                 if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff)))
 312                         err = -EFAULT;
 313                 break;
 314 
 315         case PTP_PIN_GETFUNC:
 316         case PTP_PIN_GETFUNC2:
 317                 if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
 318                         err = -EFAULT;
 319                         break;
 320                 }
 321                 if ((pd.rsv[0] || pd.rsv[1] || pd.rsv[2]
 322                                 || pd.rsv[3] || pd.rsv[4])
 323                         && cmd == PTP_PIN_GETFUNC2) {
 324                         err = -EINVAL;
 325                         break;
 326                 } else if (cmd == PTP_PIN_GETFUNC) {
 327                         pd.rsv[0] = 0;
 328                         pd.rsv[1] = 0;
 329                         pd.rsv[2] = 0;
 330                         pd.rsv[3] = 0;
 331                         pd.rsv[4] = 0;
 332                 }
 333                 pin_index = pd.index;
 334                 if (pin_index >= ops->n_pins) {
 335                         err = -EINVAL;
 336                         break;
 337                 }
 338                 pin_index = array_index_nospec(pin_index, ops->n_pins);
 339                 if (mutex_lock_interruptible(&ptp->pincfg_mux))
 340                         return -ERESTARTSYS;
 341                 pd = ops->pin_config[pin_index];
 342                 mutex_unlock(&ptp->pincfg_mux);
 343                 if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd)))
 344                         err = -EFAULT;
 345                 break;
 346 
 347         case PTP_PIN_SETFUNC:
 348         case PTP_PIN_SETFUNC2:
 349                 if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
 350                         err = -EFAULT;
 351                         break;
 352                 }
 353                 if ((pd.rsv[0] || pd.rsv[1] || pd.rsv[2]
 354                                 || pd.rsv[3] || pd.rsv[4])
 355                         && cmd == PTP_PIN_SETFUNC2) {
 356                         err = -EINVAL;
 357                         break;
 358                 } else if (cmd == PTP_PIN_SETFUNC) {
 359                         pd.rsv[0] = 0;
 360                         pd.rsv[1] = 0;
 361                         pd.rsv[2] = 0;
 362                         pd.rsv[3] = 0;
 363                         pd.rsv[4] = 0;
 364                 }
 365                 pin_index = pd.index;
 366                 if (pin_index >= ops->n_pins) {
 367                         err = -EINVAL;
 368                         break;
 369                 }
 370                 pin_index = array_index_nospec(pin_index, ops->n_pins);
 371                 if (mutex_lock_interruptible(&ptp->pincfg_mux))
 372                         return -ERESTARTSYS;
 373                 err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan);
 374                 mutex_unlock(&ptp->pincfg_mux);
 375                 break;
 376 
 377         default:
 378                 err = -ENOTTY;
 379                 break;
 380         }
 381 
 382 out:
 383         kfree(extoff);
 384         kfree(sysoff);
 385         return err;
 386 }
 387 
 388 __poll_t ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
 389 {
 390         struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 391 
 392         poll_wait(fp, &ptp->tsev_wq, wait);
 393 
 394         return queue_cnt(&ptp->tsevq) ? EPOLLIN : 0;
 395 }
 396 
 397 #define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event))
 398 
 399 ssize_t ptp_read(struct posix_clock *pc,
 400                  uint rdflags, char __user *buf, size_t cnt)
 401 {
 402         struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 403         struct timestamp_event_queue *queue = &ptp->tsevq;
 404         struct ptp_extts_event *event;
 405         unsigned long flags;
 406         size_t qcnt, i;
 407         int result;
 408 
 409         if (cnt % sizeof(struct ptp_extts_event) != 0)
 410                 return -EINVAL;
 411 
 412         if (cnt > EXTTS_BUFSIZE)
 413                 cnt = EXTTS_BUFSIZE;
 414 
 415         cnt = cnt / sizeof(struct ptp_extts_event);
 416 
 417         if (mutex_lock_interruptible(&ptp->tsevq_mux))
 418                 return -ERESTARTSYS;
 419 
 420         if (wait_event_interruptible(ptp->tsev_wq,
 421                                      ptp->defunct || queue_cnt(queue))) {
 422                 mutex_unlock(&ptp->tsevq_mux);
 423                 return -ERESTARTSYS;
 424         }
 425 
 426         if (ptp->defunct) {
 427                 mutex_unlock(&ptp->tsevq_mux);
 428                 return -ENODEV;
 429         }
 430 
 431         event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
 432         if (!event) {
 433                 mutex_unlock(&ptp->tsevq_mux);
 434                 return -ENOMEM;
 435         }
 436 
 437         spin_lock_irqsave(&queue->lock, flags);
 438 
 439         qcnt = queue_cnt(queue);
 440 
 441         if (cnt > qcnt)
 442                 cnt = qcnt;
 443 
 444         for (i = 0; i < cnt; i++) {
 445                 event[i] = queue->buf[queue->head];
 446                 queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
 447         }
 448 
 449         spin_unlock_irqrestore(&queue->lock, flags);
 450 
 451         cnt = cnt * sizeof(struct ptp_extts_event);
 452 
 453         mutex_unlock(&ptp->tsevq_mux);
 454 
 455         result = cnt;
 456         if (copy_to_user(buf, event, cnt))
 457                 result = -EFAULT;
 458 
 459         kfree(event);
 460         return result;
 461 }

/* [<][>][^][v][top][bottom][index][help] */