Blame view
drivers/spi/spidev.c
21.5 KB
814a8d50e /dev/spidevB.C in... |
1 |
/* |
ca632f556 spi: reorganize d... |
2 |
* Simple synchronous userspace interface to SPI devices |
814a8d50e /dev/spidevB.C in... |
3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
* * Copyright (C) 2006 SWAPP * Andrea Paterniani <a.paterniani@swapp-eng.it> * Copyright (C) 2007 David Brownell (simplification, cleanup) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. |
814a8d50e /dev/spidevB.C in... |
17 18 19 20 21 22 23 |
*/ #include <linux/init.h> #include <linux/module.h> #include <linux/ioctl.h> #include <linux/fs.h> #include <linux/device.h> |
b2c8daddc spi: fix refcount... |
24 |
#include <linux/err.h> |
814a8d50e /dev/spidevB.C in... |
25 26 27 28 |
#include <linux/list.h> #include <linux/errno.h> #include <linux/mutex.h> #include <linux/slab.h> |
7d48ec369 spi/spidev: Add 3... |
29 |
#include <linux/compat.h> |
880cfd43e spi: spidev: Add ... |
30 31 |
#include <linux/of.h> #include <linux/of_device.h> |
cf9f4327a spi: spidev: Add ... |
32 |
#include <linux/acpi.h> |
814a8d50e /dev/spidevB.C in... |
33 34 35 |
#include <linux/spi/spi.h> #include <linux/spi/spidev.h> |
95c63cfba spi: spidev: Fix ... |
36 |
#include <linux/uaccess.h> |
814a8d50e /dev/spidevB.C in... |
37 38 39 |
/* |
b595076a1 tree-wide: fix co... |
40 |
* This supports access to SPI devices using normal userspace I/O calls. |
814a8d50e /dev/spidevB.C in... |
41 42 |
* Note that while traditional UNIX/POSIX I/O semantics are half duplex, * and often mask message boundaries, full SPI support requires full duplex |
137f1188e spidev: fix doubl... |
43 |
* transfers. There are several kinds of internal message boundaries to |
814a8d50e /dev/spidevB.C in... |
44 45 46 47 48 49 50 51 52 53 |
* handle chipselect management and other protocol options. * * SPI has a character major number assigned. We allocate minor numbers * dynamically using a bitmask. You must use hotplug tools, such as udev * (or mdev with busybox) to create and destroy the /dev/spidevB.C device * nodes, since there is no fixed association of minor numbers with any * particular SPI bus or device. */ #define SPIDEV_MAJOR 153 /* assigned */ #define N_SPI_MINORS 32 /* ... up to 256 */ |
8ae1c9248 spidev: use DECLA... |
54 |
static DECLARE_BITMAP(minors, N_SPI_MINORS); |
814a8d50e /dev/spidevB.C in... |
55 |
|
6f166e383 spidev supports m... |
56 |
/* Bit masks for spi_device.mode management. Note that incorrect |
b55f627fe spi: new spi->mod... |
57 58 |
* settings for some settings can cause *lots* of trouble for other * devices on a shared bus: |
6f166e383 spidev supports m... |
59 |
* |
b55f627fe spi: new spi->mod... |
60 61 62 63 64 65 66 |
* - CS_HIGH ... this device will be active when it shouldn't be * - 3WIRE ... when active, it won't behave as it should * - NO_CS ... there will be no explicit message boundaries; this * is completely incompatible with the shared bus model * - READY ... transfers may proceed when they shouldn't. * * REVISIT should changing those flags be privileged? |
6f166e383 spidev supports m... |
67 68 |
*/ #define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \ |
b55f627fe spi: new spi->mod... |
69 |
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \ |
dc64d39b5 spi: spidev: Add ... |
70 71 |
| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \ | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD) |
814a8d50e /dev/spidevB.C in... |
72 73 |
struct spidev_data { |
b2c8daddc spi: fix refcount... |
74 |
dev_t devt; |
25d5cb4b0 spi: remove some ... |
75 |
spinlock_t spi_lock; |
814a8d50e /dev/spidevB.C in... |
76 77 |
struct spi_device *spi; struct list_head device_entry; |
865f6d197 spi: spidev: Use ... |
78 |
/* TX/RX buffers are NULL unless this device is open (users > 0) */ |
814a8d50e /dev/spidevB.C in... |
79 80 |
struct mutex buf_lock; unsigned users; |
865f6d197 spi: spidev: Use ... |
81 82 |
u8 *tx_buffer; u8 *rx_buffer; |
916905161 spi: spidev: Don'... |
83 |
u32 speed_hz; |
814a8d50e /dev/spidevB.C in... |
84 85 86 87 88 89 90 91 92 93 |
}; static LIST_HEAD(device_list); static DEFINE_MUTEX(device_list_lock); static unsigned bufsiz = 4096; module_param(bufsiz, uint, S_IRUGO); MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); /*-------------------------------------------------------------------------*/ |
25d5cb4b0 spi: remove some ... |
94 95 96 |
static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message) { |
25d5cb4b0 spi: remove some ... |
97 |
int status; |
98d6f4795 spi: spidev: use ... |
98 |
struct spi_device *spi; |
25d5cb4b0 spi: remove some ... |
99 100 |
spin_lock_irq(&spidev->spi_lock); |
98d6f4795 spi: spidev: use ... |
101 102 103 104 |
spi = spidev->spi; spin_unlock_irq(&spidev->spi_lock); if (spi == NULL) |
25d5cb4b0 spi: remove some ... |
105 106 |
status = -ESHUTDOWN; else |
98d6f4795 spi: spidev: use ... |
107 108 109 110 |
status = spi_sync(spi, message); if (status == 0) status = message->actual_length; |
25d5cb4b0 spi: remove some ... |
111 |
|
25d5cb4b0 spi: remove some ... |
112 113 114 115 116 117 118 |
return status; } static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { |
865f6d197 spi: spidev: Use ... |
119 |
.tx_buf = spidev->tx_buffer, |
25d5cb4b0 spi: remove some ... |
120 |
.len = len, |
916905161 spi: spidev: Don'... |
121 |
.speed_hz = spidev->speed_hz, |
25d5cb4b0 spi: remove some ... |
122 123 124 125 126 127 128 129 130 131 132 133 |
}; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m); } static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { |
865f6d197 spi: spidev: Use ... |
134 |
.rx_buf = spidev->rx_buffer, |
25d5cb4b0 spi: remove some ... |
135 |
.len = len, |
916905161 spi: spidev: Don'... |
136 |
.speed_hz = spidev->speed_hz, |
25d5cb4b0 spi: remove some ... |
137 138 139 140 141 142 143 144 145 |
}; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m); } /*-------------------------------------------------------------------------*/ |
814a8d50e /dev/spidevB.C in... |
146 147 148 149 150 |
/* Read-only message with current device setup */ static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; |
814a8d50e /dev/spidevB.C in... |
151 152 153 154 155 156 157 |
ssize_t status = 0; /* chipselect only toggles at start or end of operation */ if (count > bufsiz) return -EMSGSIZE; spidev = filp->private_data; |
814a8d50e /dev/spidevB.C in... |
158 159 |
mutex_lock(&spidev->buf_lock); |
25d5cb4b0 spi: remove some ... |
160 |
status = spidev_sync_read(spidev, count); |
4b1295b0d spi: fix the read... |
161 |
if (status > 0) { |
814a8d50e /dev/spidevB.C in... |
162 |
unsigned long missing; |
865f6d197 spi: spidev: Use ... |
163 |
missing = copy_to_user(buf, spidev->rx_buffer, status); |
4b1295b0d spi: fix the read... |
164 |
if (missing == status) |
814a8d50e /dev/spidevB.C in... |
165 166 |
status = -EFAULT; else |
4b1295b0d spi: fix the read... |
167 |
status = status - missing; |
814a8d50e /dev/spidevB.C in... |
168 169 170 171 172 173 174 175 176 177 178 179 |
} mutex_unlock(&spidev->buf_lock); return status; } /* Write-only message with current device setup */ static ssize_t spidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; |
814a8d50e /dev/spidevB.C in... |
180 181 182 183 184 185 186 187 |
ssize_t status = 0; unsigned long missing; /* chipselect only toggles at start or end of operation */ if (count > bufsiz) return -EMSGSIZE; spidev = filp->private_data; |
814a8d50e /dev/spidevB.C in... |
188 189 |
mutex_lock(&spidev->buf_lock); |
865f6d197 spi: spidev: Use ... |
190 |
missing = copy_from_user(spidev->tx_buffer, buf, count); |
95c63cfba spi: spidev: Fix ... |
191 |
if (missing == 0) |
25d5cb4b0 spi: remove some ... |
192 |
status = spidev_sync_write(spidev, count); |
95c63cfba spi: spidev: Fix ... |
193 |
else |
814a8d50e /dev/spidevB.C in... |
194 195 196 197 198 199 200 201 202 203 204 205 206 |
status = -EFAULT; mutex_unlock(&spidev->buf_lock); return status; } static int spidev_message(struct spidev_data *spidev, struct spi_ioc_transfer *u_xfers, unsigned n_xfers) { struct spi_message msg; struct spi_transfer *k_xfers; struct spi_transfer *k_tmp; struct spi_ioc_transfer *u_tmp; |
9a12bff7c spi: spidev: only... |
207 |
unsigned n, total, tx_total, rx_total; |
865f6d197 spi: spidev: Use ... |
208 |
u8 *tx_buf, *rx_buf; |
814a8d50e /dev/spidevB.C in... |
209 210 211 212 213 214 215 216 217 218 219 |
int status = -EFAULT; spi_message_init(&msg); k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); if (k_xfers == NULL) return -ENOMEM; /* Construct spi_message, copying any tx data to bounce buffer. * We walk the array of user-provided transfers, using each one * to initialize a kernel version of the same transfer. */ |
865f6d197 spi: spidev: Use ... |
220 221 |
tx_buf = spidev->tx_buffer; rx_buf = spidev->rx_buffer; |
814a8d50e /dev/spidevB.C in... |
222 |
total = 0; |
9a12bff7c spi: spidev: only... |
223 224 |
tx_total = 0; rx_total = 0; |
814a8d50e /dev/spidevB.C in... |
225 226 227 228 |
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; n; n--, k_tmp++, u_tmp++) { k_tmp->len = u_tmp->len; |
da90fa8ff spi/spidev: check... |
229 |
total += k_tmp->len; |
9a12bff7c spi: spidev: only... |
230 231 |
/* Since the function returns the total length of transfers * on success, restrict the total to positive int values to |
f20fbaad7 spi: spidev: fix ... |
232 233 |
* avoid the return value looking like an error. Also check * each transfer length to avoid arithmetic overflow. |
9a12bff7c spi: spidev: only... |
234 |
*/ |
f20fbaad7 spi: spidev: fix ... |
235 |
if (total > INT_MAX || k_tmp->len > INT_MAX) { |
da90fa8ff spi/spidev: check... |
236 237 238 |
status = -EMSGSIZE; goto done; } |
814a8d50e /dev/spidevB.C in... |
239 |
if (u_tmp->rx_buf) { |
9a12bff7c spi: spidev: only... |
240 241 242 243 244 245 |
/* this transfer needs space in RX bounce buffer */ rx_total += k_tmp->len; if (rx_total > bufsiz) { status = -EMSGSIZE; goto done; } |
865f6d197 spi: spidev: Use ... |
246 |
k_tmp->rx_buf = rx_buf; |
9a12bff7c spi: spidev: only... |
247 |
rx_buf += k_tmp->len; |
814a8d50e /dev/spidevB.C in... |
248 249 |
} if (u_tmp->tx_buf) { |
9a12bff7c spi: spidev: only... |
250 251 252 253 254 255 |
/* this transfer needs space in TX bounce buffer */ tx_total += k_tmp->len; if (tx_total > bufsiz) { status = -EMSGSIZE; goto done; } |
865f6d197 spi: spidev: Use ... |
256 257 |
k_tmp->tx_buf = tx_buf; if (copy_from_user(tx_buf, (const u8 __user *) |
142956af5 fix abuses of ptr... |
258 |
(uintptr_t) u_tmp->tx_buf, |
814a8d50e /dev/spidevB.C in... |
259 260 |
u_tmp->len)) goto done; |
9a12bff7c spi: spidev: only... |
261 |
tx_buf += k_tmp->len; |
814a8d50e /dev/spidevB.C in... |
262 |
} |
814a8d50e /dev/spidevB.C in... |
263 264 |
k_tmp->cs_change = !!u_tmp->cs_change; |
dc64d39b5 spi: spidev: Add ... |
265 266 |
k_tmp->tx_nbits = u_tmp->tx_nbits; k_tmp->rx_nbits = u_tmp->rx_nbits; |
814a8d50e /dev/spidevB.C in... |
267 268 269 |
k_tmp->bits_per_word = u_tmp->bits_per_word; k_tmp->delay_usecs = u_tmp->delay_usecs; k_tmp->speed_hz = u_tmp->speed_hz; |
916905161 spi: spidev: Don'... |
270 271 |
if (!k_tmp->speed_hz) k_tmp->speed_hz = spidev->speed_hz; |
814a8d50e /dev/spidevB.C in... |
272 |
#ifdef VERBOSE |
41df70d9a spi: fix spidev c... |
273 |
dev_dbg(&spidev->spi->dev, |
f15c58410 spi: spidev: Use ... |
274 275 |
" xfer len %u %s%s%s%dbits %u usec %uHz ", |
814a8d50e /dev/spidevB.C in... |
276 277 278 279 |
u_tmp->len, u_tmp->rx_buf ? "rx " : "", u_tmp->tx_buf ? "tx " : "", u_tmp->cs_change ? "cs " : "", |
41df70d9a spi: fix spidev c... |
280 |
u_tmp->bits_per_word ? : spidev->spi->bits_per_word, |
814a8d50e /dev/spidevB.C in... |
281 |
u_tmp->delay_usecs, |
41df70d9a spi: fix spidev c... |
282 |
u_tmp->speed_hz ? : spidev->spi->max_speed_hz); |
814a8d50e /dev/spidevB.C in... |
283 284 285 |
#endif spi_message_add_tail(k_tmp, &msg); } |
25d5cb4b0 spi: remove some ... |
286 |
status = spidev_sync(spidev, &msg); |
814a8d50e /dev/spidevB.C in... |
287 288 289 290 |
if (status < 0) goto done; /* copy any rx data out of bounce buffer */ |
865f6d197 spi: spidev: Use ... |
291 |
rx_buf = spidev->rx_buffer; |
814a8d50e /dev/spidevB.C in... |
292 293 |
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { if (u_tmp->rx_buf) { |
251d59515 spidev: quit mess... |
294 |
if (copy_to_user((u8 __user *) |
865f6d197 spi: spidev: Use ... |
295 |
(uintptr_t) u_tmp->rx_buf, rx_buf, |
814a8d50e /dev/spidevB.C in... |
296 297 298 299 |
u_tmp->len)) { status = -EFAULT; goto done; } |
9a12bff7c spi: spidev: only... |
300 |
rx_buf += u_tmp->len; |
814a8d50e /dev/spidevB.C in... |
301 |
} |
814a8d50e /dev/spidevB.C in... |
302 303 304 305 |
} status = total; done: |
814a8d50e /dev/spidevB.C in... |
306 307 308 |
kfree(k_xfers); return status; } |
7782a1a94 spi: spidev: Conv... |
309 310 311 312 |
static struct spi_ioc_transfer * spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc, unsigned *n_ioc) { |
7782a1a94 spi: spidev: Conv... |
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
u32 tmp; /* Check type, command number and direction */ if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC || _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) || _IOC_DIR(cmd) != _IOC_WRITE) return ERR_PTR(-ENOTTY); tmp = _IOC_SIZE(cmd); if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) return ERR_PTR(-EINVAL); *n_ioc = tmp / sizeof(struct spi_ioc_transfer); if (*n_ioc == 0) return NULL; /* copy into scratch area */ |
f7929436a spi: spidev: use ... |
329 |
return memdup_user(u_ioc, tmp); |
7782a1a94 spi: spidev: Conv... |
330 |
} |
4ef754b7d spidev: BKL removal |
331 332 |
static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
814a8d50e /dev/spidevB.C in... |
333 |
{ |
814a8d50e /dev/spidevB.C in... |
334 335 336 337 338 339 340 341 342 343 |
int retval = 0; struct spidev_data *spidev; struct spi_device *spi; u32 tmp; unsigned n_ioc; struct spi_ioc_transfer *ioc; /* Check type and command number */ if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) return -ENOTTY; |
25d5cb4b0 spi: remove some ... |
344 345 346 |
/* guard against device removal before, or while, * we issue this ioctl. */ |
814a8d50e /dev/spidevB.C in... |
347 |
spidev = filp->private_data; |
25d5cb4b0 spi: remove some ... |
348 349 350 351 352 353 |
spin_lock_irq(&spidev->spi_lock); spi = spi_dev_get(spidev->spi); spin_unlock_irq(&spidev->spi_lock); if (spi == NULL) return -ESHUTDOWN; |
814a8d50e /dev/spidevB.C in... |
354 |
|
4ef754b7d spidev: BKL removal |
355 356 357 358 359 360 361 |
/* use the buffer lock here for triple duty: * - prevent I/O (from us) so calling spi_setup() is safe; * - prevent concurrent SPI_IOC_WR_* from morphing * data fields while SPI_IOC_RD_* reads them; * - SPI_IOC_MESSAGE needs the buffer locked "normally". */ mutex_lock(&spidev->buf_lock); |
814a8d50e /dev/spidevB.C in... |
362 363 364 |
switch (cmd) { /* read requests */ case SPI_IOC_RD_MODE: |
251d59515 spidev: quit mess... |
365 |
retval = put_user(spi->mode & SPI_MODE_MASK, |
814a8d50e /dev/spidevB.C in... |
366 367 |
(__u8 __user *)arg); break; |
dc64d39b5 spi: spidev: Add ... |
368 |
case SPI_IOC_RD_MODE32: |
251d59515 spidev: quit mess... |
369 |
retval = put_user(spi->mode & SPI_MODE_MASK, |
dc64d39b5 spi: spidev: Add ... |
370 371 |
(__u32 __user *)arg); break; |
814a8d50e /dev/spidevB.C in... |
372 |
case SPI_IOC_RD_LSB_FIRST: |
251d59515 spidev: quit mess... |
373 |
retval = put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0, |
814a8d50e /dev/spidevB.C in... |
374 375 376 |
(__u8 __user *)arg); break; case SPI_IOC_RD_BITS_PER_WORD: |
251d59515 spidev: quit mess... |
377 |
retval = put_user(spi->bits_per_word, (__u8 __user *)arg); |
814a8d50e /dev/spidevB.C in... |
378 379 |
break; case SPI_IOC_RD_MAX_SPEED_HZ: |
251d59515 spidev: quit mess... |
380 |
retval = put_user(spidev->speed_hz, (__u32 __user *)arg); |
814a8d50e /dev/spidevB.C in... |
381 382 383 384 |
break; /* write requests */ case SPI_IOC_WR_MODE: |
dc64d39b5 spi: spidev: Add ... |
385 386 |
case SPI_IOC_WR_MODE32: if (cmd == SPI_IOC_WR_MODE) |
251d59515 spidev: quit mess... |
387 |
retval = get_user(tmp, (u8 __user *)arg); |
dc64d39b5 spi: spidev: Add ... |
388 |
else |
251d59515 spidev: quit mess... |
389 |
retval = get_user(tmp, (u32 __user *)arg); |
814a8d50e /dev/spidevB.C in... |
390 |
if (retval == 0) { |
e6456186c spi: spidev: Rest... |
391 |
u32 save = spi->mode; |
814a8d50e /dev/spidevB.C in... |
392 393 394 395 396 397 398 |
if (tmp & ~SPI_MODE_MASK) { retval = -EINVAL; break; } tmp |= spi->mode & ~SPI_MODE_MASK; |
dc64d39b5 spi: spidev: Add ... |
399 |
spi->mode = (u16)tmp; |
814a8d50e /dev/spidevB.C in... |
400 401 402 403 |
retval = spi_setup(spi); if (retval < 0) spi->mode = save; else |
dc64d39b5 spi: spidev: Add ... |
404 405 |
dev_dbg(&spi->dev, "spi mode %x ", tmp); |
814a8d50e /dev/spidevB.C in... |
406 407 408 |
} break; case SPI_IOC_WR_LSB_FIRST: |
251d59515 spidev: quit mess... |
409 |
retval = get_user(tmp, (__u8 __user *)arg); |
814a8d50e /dev/spidevB.C in... |
410 |
if (retval == 0) { |
e6456186c spi: spidev: Rest... |
411 |
u32 save = spi->mode; |
814a8d50e /dev/spidevB.C in... |
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 |
if (tmp) spi->mode |= SPI_LSB_FIRST; else spi->mode &= ~SPI_LSB_FIRST; retval = spi_setup(spi); if (retval < 0) spi->mode = save; else dev_dbg(&spi->dev, "%csb first ", tmp ? 'l' : 'm'); } break; case SPI_IOC_WR_BITS_PER_WORD: |
251d59515 spidev: quit mess... |
427 |
retval = get_user(tmp, (__u8 __user *)arg); |
814a8d50e /dev/spidevB.C in... |
428 429 430 431 432 433 434 435 436 437 438 439 440 |
if (retval == 0) { u8 save = spi->bits_per_word; spi->bits_per_word = tmp; retval = spi_setup(spi); if (retval < 0) spi->bits_per_word = save; else dev_dbg(&spi->dev, "%d bits per word ", tmp); } break; case SPI_IOC_WR_MAX_SPEED_HZ: |
251d59515 spidev: quit mess... |
441 |
retval = get_user(tmp, (__u32 __user *)arg); |
814a8d50e /dev/spidevB.C in... |
442 443 444 445 446 |
if (retval == 0) { u32 save = spi->max_speed_hz; spi->max_speed_hz = tmp; retval = spi_setup(spi); |
916905161 spi: spidev: Don'... |
447 448 |
if (retval >= 0) spidev->speed_hz = tmp; |
814a8d50e /dev/spidevB.C in... |
449 450 451 |
else dev_dbg(&spi->dev, "%d Hz (max) ", tmp); |
916905161 spi: spidev: Don'... |
452 |
spi->max_speed_hz = save; |
814a8d50e /dev/spidevB.C in... |
453 454 455 456 457 |
} break; default: /* segmented and/or full-duplex I/O request */ |
7782a1a94 spi: spidev: Conv... |
458 459 460 461 462 |
/* Check message and copy into scratch area */ ioc = spidev_get_ioc_message(cmd, (struct spi_ioc_transfer __user *)arg, &n_ioc); if (IS_ERR(ioc)) { retval = PTR_ERR(ioc); |
814a8d50e /dev/spidevB.C in... |
463 464 |
break; } |
7782a1a94 spi: spidev: Conv... |
465 466 |
if (!ioc) break; /* n_ioc is also 0 */ |
814a8d50e /dev/spidevB.C in... |
467 468 469 470 471 472 |
/* translate to spi_message, execute */ retval = spidev_message(spidev, ioc, n_ioc); kfree(ioc); break; } |
4ef754b7d spidev: BKL removal |
473 474 |
mutex_unlock(&spidev->buf_lock); |
25d5cb4b0 spi: remove some ... |
475 |
spi_dev_put(spi); |
814a8d50e /dev/spidevB.C in... |
476 477 |
return retval; } |
7d48ec369 spi/spidev: Add 3... |
478 479 |
#ifdef CONFIG_COMPAT static long |
7782a1a94 spi: spidev: Conv... |
480 481 482 483 484 485 486 487 488 489 490 |
spidev_compat_ioc_message(struct file *filp, unsigned int cmd, unsigned long arg) { struct spi_ioc_transfer __user *u_ioc; int retval = 0; struct spidev_data *spidev; struct spi_device *spi; unsigned n_ioc, n; struct spi_ioc_transfer *ioc; u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg); |
7782a1a94 spi: spidev: Conv... |
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
/* guard against device removal before, or while, * we issue this ioctl. */ spidev = filp->private_data; spin_lock_irq(&spidev->spi_lock); spi = spi_dev_get(spidev->spi); spin_unlock_irq(&spidev->spi_lock); if (spi == NULL) return -ESHUTDOWN; /* SPI_IOC_MESSAGE needs the buffer locked "normally" */ mutex_lock(&spidev->buf_lock); /* Check message and copy into scratch area */ ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc); if (IS_ERR(ioc)) { retval = PTR_ERR(ioc); goto done; } if (!ioc) goto done; /* n_ioc is also 0 */ /* Convert buffer pointers */ for (n = 0; n < n_ioc; n++) { ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf); ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf); } /* translate to spi_message, execute */ retval = spidev_message(spidev, ioc, n_ioc); kfree(ioc); done: mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); return retval; } static long |
7d48ec369 spi/spidev: Add 3... |
532 533 |
spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { |
7782a1a94 spi: spidev: Conv... |
534 535 536 537 |
if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC && _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0)) && _IOC_DIR(cmd) == _IOC_WRITE) return spidev_compat_ioc_message(filp, cmd, arg); |
7d48ec369 spi/spidev: Add 3... |
538 539 540 541 542 |
return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); } #else #define spidev_compat_ioctl NULL #endif /* CONFIG_COMPAT */ |
814a8d50e /dev/spidevB.C in... |
543 544 545 546 547 548 549 550 |
static int spidev_open(struct inode *inode, struct file *filp) { struct spidev_data *spidev; int status = -ENXIO; mutex_lock(&device_list_lock); list_for_each_entry(spidev, &device_list, device_entry) { |
b2c8daddc spi: fix refcount... |
551 |
if (spidev->devt == inode->i_rdev) { |
814a8d50e /dev/spidevB.C in... |
552 553 554 555 |
status = 0; break; } } |
865f6d197 spi: spidev: Use ... |
556 557 558 559 560 561 562 563 564 565 |
if (status) { pr_debug("spidev: nothing for minor %d ", iminor(inode)); goto err_find_dev; } if (!spidev->tx_buffer) { spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); if (!spidev->tx_buffer) { |
89a635667 spi: spidev: fix ... |
566 567 568 |
dev_dbg(&spidev->spi->dev, "open/ENOMEM "); status = -ENOMEM; |
865f6d197 spi: spidev: Use ... |
569 |
goto err_find_dev; |
814a8d50e /dev/spidevB.C in... |
570 |
} |
89a635667 spi: spidev: fix ... |
571 |
} |
865f6d197 spi: spidev: Use ... |
572 573 574 575 576 577 578 579 |
if (!spidev->rx_buffer) { spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); if (!spidev->rx_buffer) { dev_dbg(&spidev->spi->dev, "open/ENOMEM "); status = -ENOMEM; goto err_alloc_rx_buf; |
814a8d50e /dev/spidevB.C in... |
580 |
} |
865f6d197 spi: spidev: Use ... |
581 582 583 584 585 |
} spidev->users++; filp->private_data = spidev; nonseekable_open(inode, filp); |
814a8d50e /dev/spidevB.C in... |
586 587 |
mutex_unlock(&device_list_lock); |
865f6d197 spi: spidev: Use ... |
588 589 590 591 592 593 594 |
return 0; err_alloc_rx_buf: kfree(spidev->tx_buffer); spidev->tx_buffer = NULL; err_find_dev: mutex_unlock(&device_list_lock); |
814a8d50e /dev/spidevB.C in... |
595 596 597 598 599 600 |
return status; } static int spidev_release(struct inode *inode, struct file *filp) { struct spidev_data *spidev; |
814a8d50e /dev/spidevB.C in... |
601 602 603 604 |
mutex_lock(&device_list_lock); spidev = filp->private_data; filp->private_data = NULL; |
b2c8daddc spi: fix refcount... |
605 606 |
/* last close? */ |
814a8d50e /dev/spidevB.C in... |
607 608 |
spidev->users--; if (!spidev->users) { |
b2c8daddc spi: fix refcount... |
609 |
int dofree; |
865f6d197 spi: spidev: Use ... |
610 611 612 613 614 |
kfree(spidev->tx_buffer); spidev->tx_buffer = NULL; kfree(spidev->rx_buffer); spidev->rx_buffer = NULL; |
b2c8daddc spi: fix refcount... |
615 |
|
56ea1075e spi: spidev: Hold... |
616 |
spin_lock_irq(&spidev->spi_lock); |
dd85ebf68 spi: spidev: fix ... |
617 618 |
if (spidev->spi) spidev->speed_hz = spidev->spi->max_speed_hz; |
916905161 spi: spidev: Don'... |
619 |
|
b2c8daddc spi: fix refcount... |
620 |
/* ... after we unbound from the underlying device? */ |
b2c8daddc spi: fix refcount... |
621 622 623 624 625 |
dofree = (spidev->spi == NULL); spin_unlock_irq(&spidev->spi_lock); if (dofree) kfree(spidev); |
814a8d50e /dev/spidevB.C in... |
626 627 |
} mutex_unlock(&device_list_lock); |
99472cc08 spi: spidev: Remo... |
628 |
return 0; |
814a8d50e /dev/spidevB.C in... |
629 |
} |
828c09509 const: constify r... |
630 |
static const struct file_operations spidev_fops = { |
814a8d50e /dev/spidevB.C in... |
631 632 633 634 635 636 637 |
.owner = THIS_MODULE, /* REVISIT switch to aio primitives, so that userspace * gets more complete API coverage. It'll simplify things * too, except for the locking. */ .write = spidev_write, .read = spidev_read, |
4ef754b7d spidev: BKL removal |
638 |
.unlocked_ioctl = spidev_ioctl, |
7d48ec369 spi/spidev: Add 3... |
639 |
.compat_ioctl = spidev_compat_ioctl, |
814a8d50e /dev/spidevB.C in... |
640 641 |
.open = spidev_open, .release = spidev_release, |
6038f373a llseek: automatic... |
642 |
.llseek = no_llseek, |
814a8d50e /dev/spidevB.C in... |
643 644 645 646 647 648 649 650 |
}; /*-------------------------------------------------------------------------*/ /* The main reason to have this class is to make mdev/udev create the * /dev/spidevB.C character device nodes exposing our userspace API. * It also simplifies memory management. */ |
b2c8daddc spi: fix refcount... |
651 |
static struct class *spidev_class; |
814a8d50e /dev/spidevB.C in... |
652 |
|
956b200a8 spi: spidev: Warn... |
653 654 655 |
#ifdef CONFIG_OF static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv" }, |
6fec919b6 spi: spidev: add ... |
656 |
{ .compatible = "lineartechnology,ltc2488" }, |
144235ea7 spi: spidev: Add ... |
657 |
{ .compatible = "ge,achc" }, |
91b463463 spi: spidev: Add ... |
658 |
{ .compatible = "semtech,sx1301" }, |
956b200a8 spi: spidev: Warn... |
659 660 661 662 |
{}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); #endif |
cf9f4327a spi: spidev: Add ... |
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 |
#ifdef CONFIG_ACPI /* Dummy SPI devices not to be used in production systems */ #define SPIDEV_ACPI_DUMMY 1 static const struct acpi_device_id spidev_acpi_ids[] = { /* * The ACPI SPT000* devices are only meant for development and * testing. Systems used in production should have a proper ACPI * description of the connected peripheral and they should also use * a proper driver instead of poking directly to the SPI bus. */ { "SPT0001", SPIDEV_ACPI_DUMMY }, { "SPT0002", SPIDEV_ACPI_DUMMY }, { "SPT0003", SPIDEV_ACPI_DUMMY }, {}, }; MODULE_DEVICE_TABLE(acpi, spidev_acpi_ids); static void spidev_probe_acpi(struct spi_device *spi) { const struct acpi_device_id *id; if (!has_acpi_companion(&spi->dev)) return; id = acpi_match_device(spidev_acpi_ids, &spi->dev); if (WARN_ON(!id)) return; if (id->driver_data == SPIDEV_ACPI_DUMMY) dev_warn(&spi->dev, "do not use this driver in production systems! "); } #else static inline void spidev_probe_acpi(struct spi_device *spi) {} #endif |
814a8d50e /dev/spidevB.C in... |
700 |
/*-------------------------------------------------------------------------*/ |
fd4a319bc spi: Remove HOTPL... |
701 |
static int spidev_probe(struct spi_device *spi) |
814a8d50e /dev/spidevB.C in... |
702 703 704 705 |
{ struct spidev_data *spidev; int status; unsigned long minor; |
956b200a8 spi: spidev: Warn... |
706 707 |
/* * spidev should never be referenced in DT without a specific |
ffe228882 spi: spidev: Fix ... |
708 |
* compatible string, it is a Linux implementation thing |
956b200a8 spi: spidev: Warn... |
709 710 711 712 713 714 715 716 |
* rather than a description of the hardware. */ if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { dev_err(&spi->dev, "buggy DT: spidev listed directly in DT "); WARN_ON(spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)); } |
cf9f4327a spi: spidev: Add ... |
717 |
spidev_probe_acpi(spi); |
814a8d50e /dev/spidevB.C in... |
718 719 720 721 722 723 724 |
/* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) return -ENOMEM; /* Initialize the driver data */ spidev->spi = spi; |
25d5cb4b0 spi: remove some ... |
725 |
spin_lock_init(&spidev->spi_lock); |
814a8d50e /dev/spidevB.C in... |
726 727 728 729 730 731 732 733 |
mutex_init(&spidev->buf_lock); INIT_LIST_HEAD(&spidev->device_entry); /* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */ mutex_lock(&device_list_lock); |
0a4dd7783 spi: fix spidev f... |
734 |
minor = find_first_zero_bit(minors, N_SPI_MINORS); |
814a8d50e /dev/spidevB.C in... |
735 |
if (minor < N_SPI_MINORS) { |
b2c8daddc spi: fix refcount... |
736 737 738 |
struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor); |
a9b12619f device create: mi... |
739 740 741 |
dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "spidev%d.%d", spi->master->bus_num, spi->chip_select); |
8c6ffba0e PTR_RET is now PT... |
742 |
status = PTR_ERR_OR_ZERO(dev); |
814a8d50e /dev/spidevB.C in... |
743 744 745 746 747 748 749 |
} else { dev_dbg(&spi->dev, "no minor number available! "); status = -ENODEV; } if (status == 0) { set_bit(minor, minors); |
814a8d50e /dev/spidevB.C in... |
750 751 752 |
list_add(&spidev->device_entry, &device_list); } mutex_unlock(&device_list_lock); |
916905161 spi: spidev: Don'... |
753 |
spidev->speed_hz = spi->max_speed_hz; |
aaacf4bb5 spi: avoid spidev... |
754 755 756 |
if (status == 0) spi_set_drvdata(spi, spidev); else |
814a8d50e /dev/spidevB.C in... |
757 758 759 760 |
kfree(spidev); return status; } |
fd4a319bc spi: Remove HOTPL... |
761 |
static int spidev_remove(struct spi_device *spi) |
814a8d50e /dev/spidevB.C in... |
762 |
{ |
b2c8daddc spi: fix refcount... |
763 |
struct spidev_data *spidev = spi_get_drvdata(spi); |
814a8d50e /dev/spidevB.C in... |
764 |
|
25d5cb4b0 spi: remove some ... |
765 766 767 768 |
/* make sure ops on existing fds can abort cleanly */ spin_lock_irq(&spidev->spi_lock); spidev->spi = NULL; spin_unlock_irq(&spidev->spi_lock); |
814a8d50e /dev/spidevB.C in... |
769 |
|
25d5cb4b0 spi: remove some ... |
770 771 |
/* prevent new opens */ mutex_lock(&device_list_lock); |
814a8d50e /dev/spidevB.C in... |
772 |
list_del(&spidev->device_entry); |
b2c8daddc spi: fix refcount... |
773 774 775 776 |
device_destroy(spidev_class, spidev->devt); clear_bit(MINOR(spidev->devt), minors); if (spidev->users == 0) kfree(spidev); |
814a8d50e /dev/spidevB.C in... |
777 778 779 780 |
mutex_unlock(&device_list_lock); return 0; } |
db389b614 spidev: add prope... |
781 |
static struct spi_driver spidev_spi_driver = { |
814a8d50e /dev/spidevB.C in... |
782 783 |
.driver = { .name = "spidev", |
880cfd43e spi: spidev: Add ... |
784 |
.of_match_table = of_match_ptr(spidev_dt_ids), |
cf9f4327a spi: spidev: Add ... |
785 |
.acpi_match_table = ACPI_PTR(spidev_acpi_ids), |
814a8d50e /dev/spidevB.C in... |
786 787 |
}, .probe = spidev_probe, |
fd4a319bc spi: Remove HOTPL... |
788 |
.remove = spidev_remove, |
814a8d50e /dev/spidevB.C in... |
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 |
/* NOTE: suspend/resume methods are not necessary here. * We don't do anything except pass the requests to/from * the underlying controller. The refrigerator handles * most issues; the controller driver handles the rest. */ }; /*-------------------------------------------------------------------------*/ static int __init spidev_init(void) { int status; /* Claim our 256 reserved device numbers. Then register a class * that will key udev/mdev to add/remove /dev nodes. Last, register * the driver which manages those device numbers. */ BUILD_BUG_ON(N_SPI_MINORS > 256); status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); if (status < 0) return status; |
b2c8daddc spi: fix refcount... |
811 812 |
spidev_class = class_create(THIS_MODULE, "spidev"); if (IS_ERR(spidev_class)) { |
db389b614 spidev: add prope... |
813 |
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); |
b2c8daddc spi: fix refcount... |
814 |
return PTR_ERR(spidev_class); |
814a8d50e /dev/spidevB.C in... |
815 |
} |
db389b614 spidev: add prope... |
816 |
status = spi_register_driver(&spidev_spi_driver); |
814a8d50e /dev/spidevB.C in... |
817 |
if (status < 0) { |
b2c8daddc spi: fix refcount... |
818 |
class_destroy(spidev_class); |
db389b614 spidev: add prope... |
819 |
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); |
814a8d50e /dev/spidevB.C in... |
820 821 822 823 824 825 826 |
} return status; } module_init(spidev_init); static void __exit spidev_exit(void) { |
db389b614 spidev: add prope... |
827 |
spi_unregister_driver(&spidev_spi_driver); |
b2c8daddc spi: fix refcount... |
828 |
class_destroy(spidev_class); |
db389b614 spidev: add prope... |
829 |
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); |
814a8d50e /dev/spidevB.C in... |
830 831 832 833 834 835 |
} module_exit(spidev_exit); MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>"); MODULE_DESCRIPTION("User mode SPI device interface"); MODULE_LICENSE("GPL"); |
e0626e384 spi: prefix modal... |
836 |
MODULE_ALIAS("spi:spidev"); |