1 /* 2 * PTP 1588 clock support - sysfs interface. 3 * 4 * Copyright (C) 2010 OMICRON electronics GmbH 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 #include <linux/capability.h> 21 22 #include "ptp_private.h" 23 24 static ssize_t clock_name_show(struct device *dev, 25 struct device_attribute *attr, char *page) 26 { 27 struct ptp_clock *ptp = dev_get_drvdata(dev); 28 return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name); 29 } 30 31 #define PTP_SHOW_INT(name) \ 32 static ssize_t name##_show(struct device *dev, \ 33 struct device_attribute *attr, char *page) \ 34 { \ 35 struct ptp_clock *ptp = dev_get_drvdata(dev); \ 36 return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->name); \ 37 } 38 39 PTP_SHOW_INT(max_adj); 40 PTP_SHOW_INT(n_alarm); 41 PTP_SHOW_INT(n_ext_ts); 42 PTP_SHOW_INT(n_per_out); 43 PTP_SHOW_INT(pps); 44 45 #define PTP_RO_ATTR(_var, _name) { \ 46 .attr = { .name = __stringify(_name), .mode = 0444 }, \ 47 .show = _var##_show, \ 48 } 49 50 struct device_attribute ptp_dev_attrs[] = { 51 PTP_RO_ATTR(clock_name, clock_name), 52 PTP_RO_ATTR(max_adj, max_adjustment), 53 PTP_RO_ATTR(n_alarm, n_alarms), 54 PTP_RO_ATTR(n_ext_ts, n_external_timestamps), 55 PTP_RO_ATTR(n_per_out, n_periodic_outputs), 56 PTP_RO_ATTR(pps, pps_available), 57 __ATTR_NULL, 58 }; 59 60 static ssize_t extts_enable_store(struct device *dev, 61 struct device_attribute *attr, 62 const char *buf, size_t count) 63 { 64 struct ptp_clock *ptp = dev_get_drvdata(dev); 65 struct ptp_clock_info *ops = ptp->info; 66 struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS }; 67 int cnt, enable; 68 int err = -EINVAL; 69 70 cnt = sscanf(buf, "%u %d", &req.extts.index, &enable); 71 if (cnt != 2) 72 goto out; 73 if (req.extts.index >= ops->n_ext_ts) 74 goto out; 75 76 err = ops->enable(ops, &req, enable ? 1 : 0); 77 if (err) 78 goto out; 79 80 return count; 81 out: 82 return err; 83 } 84 85 static ssize_t extts_fifo_show(struct device *dev, 86 struct device_attribute *attr, char *page) 87 { 88 struct ptp_clock *ptp = dev_get_drvdata(dev); 89 struct timestamp_event_queue *queue = &ptp->tsevq; 90 struct ptp_extts_event event; 91 unsigned long flags; 92 size_t qcnt; 93 int cnt = 0; 94 95 memset(&event, 0, sizeof(event)); 96 97 if (mutex_lock_interruptible(&ptp->tsevq_mux)) 98 return -ERESTARTSYS; 99 100 spin_lock_irqsave(&queue->lock, flags); 101 qcnt = queue_cnt(queue); 102 if (qcnt) { 103 event = queue->buf[queue->head]; 104 queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; 105 } 106 spin_unlock_irqrestore(&queue->lock, flags); 107 108 if (!qcnt) 109 goto out; 110 111 cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n", 112 event.index, event.t.sec, event.t.nsec); 113 out: 114 mutex_unlock(&ptp->tsevq_mux); 115 return cnt; 116 } 117 118 static ssize_t period_store(struct device *dev, 119 struct device_attribute *attr, 120 const char *buf, size_t count) 121 { 122 struct ptp_clock *ptp = dev_get_drvdata(dev); 123 struct ptp_clock_info *ops = ptp->info; 124 struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT }; 125 int cnt, enable, err = -EINVAL; 126 127 cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index, 128 &req.perout.start.sec, &req.perout.start.nsec, 129 &req.perout.period.sec, &req.perout.period.nsec); 130 if (cnt != 5) 131 goto out; 132 if (req.perout.index >= ops->n_per_out) 133 goto out; 134 135 enable = req.perout.period.sec || req.perout.period.nsec; 136 err = ops->enable(ops, &req, enable); 137 if (err) 138 goto out; 139 140 return count; 141 out: 142 return err; 143 } 144 145 static ssize_t pps_enable_store(struct device *dev, 146 struct device_attribute *attr, 147 const char *buf, size_t count) 148 { 149 struct ptp_clock *ptp = dev_get_drvdata(dev); 150 struct ptp_clock_info *ops = ptp->info; 151 struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS }; 152 int cnt, enable; 153 int err = -EINVAL; 154 155 if (!capable(CAP_SYS_TIME)) 156 return -EPERM; 157 158 cnt = sscanf(buf, "%d", &enable); 159 if (cnt != 1) 160 goto out; 161 162 err = ops->enable(ops, &req, enable ? 1 : 0); 163 if (err) 164 goto out; 165 166 return count; 167 out: 168 return err; 169 } 170 171 static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); 172 static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); 173 static DEVICE_ATTR(period, 0220, NULL, period_store); 174 static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store); 175 176 int ptp_cleanup_sysfs(struct ptp_clock *ptp) 177 { 178 struct device *dev = ptp->dev; 179 struct ptp_clock_info *info = ptp->info; 180 181 if (info->n_ext_ts) { 182 device_remove_file(dev, &dev_attr_extts_enable); 183 device_remove_file(dev, &dev_attr_fifo); 184 } 185 if (info->n_per_out) 186 device_remove_file(dev, &dev_attr_period); 187 188 if (info->pps) 189 device_remove_file(dev, &dev_attr_pps_enable); 190 191 return 0; 192 } 193 194 int ptp_populate_sysfs(struct ptp_clock *ptp) 195 { 196 struct device *dev = ptp->dev; 197 struct ptp_clock_info *info = ptp->info; 198 int err; 199 200 if (info->n_ext_ts) { 201 err = device_create_file(dev, &dev_attr_extts_enable); 202 if (err) 203 goto out1; 204 err = device_create_file(dev, &dev_attr_fifo); 205 if (err) 206 goto out2; 207 } 208 if (info->n_per_out) { 209 err = device_create_file(dev, &dev_attr_period); 210 if (err) 211 goto out3; 212 } 213 if (info->pps) { 214 err = device_create_file(dev, &dev_attr_pps_enable); 215 if (err) 216 goto out4; 217 } 218 return 0; 219 out4: 220 if (info->n_per_out) 221 device_remove_file(dev, &dev_attr_period); 222 out3: 223 if (info->n_ext_ts) 224 device_remove_file(dev, &dev_attr_fifo); 225 out2: 226 if (info->n_ext_ts) 227 device_remove_file(dev, &dev_attr_extts_enable); 228 out1: 229 return err; 230 } 231