15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
200a2430fSAndrzej Pietrasiewicz /*
300a2430fSAndrzej Pietrasiewicz * f_fs.c -- user mode file system API for USB composite function controllers
400a2430fSAndrzej Pietrasiewicz *
500a2430fSAndrzej Pietrasiewicz * Copyright (C) 2010 Samsung Electronics
600a2430fSAndrzej Pietrasiewicz * Author: Michal Nazarewicz <mina86@mina86.com>
700a2430fSAndrzej Pietrasiewicz *
800a2430fSAndrzej Pietrasiewicz * Based on inode.c (GadgetFS) which was:
900a2430fSAndrzej Pietrasiewicz * Copyright (C) 2003-2004 David Brownell
1000a2430fSAndrzej Pietrasiewicz * Copyright (C) 2003 Agilent Technologies
1100a2430fSAndrzej Pietrasiewicz */
1200a2430fSAndrzej Pietrasiewicz
1300a2430fSAndrzej Pietrasiewicz
1400a2430fSAndrzej Pietrasiewicz /* #define DEBUG */
1500a2430fSAndrzej Pietrasiewicz /* #define VERBOSE_DEBUG */
1600a2430fSAndrzej Pietrasiewicz
1700a2430fSAndrzej Pietrasiewicz #include <linux/blkdev.h>
1800a2430fSAndrzej Pietrasiewicz #include <linux/pagemap.h>
1900a2430fSAndrzej Pietrasiewicz #include <linux/export.h>
20dec90f61SDavid Howells #include <linux/fs_parser.h>
2100a2430fSAndrzej Pietrasiewicz #include <linux/hid.h>
22772a7a72SAndrzej Pietrasiewicz #include <linux/mm.h>
2300a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
24772a7a72SAndrzej Pietrasiewicz #include <linux/scatterlist.h>
25174cd4b1SIngo Molnar #include <linux/sched/signal.h>
26e2e40f2cSChristoph Hellwig #include <linux/uio.h>
27772a7a72SAndrzej Pietrasiewicz #include <linux/vmalloc.h>
2800a2430fSAndrzej Pietrasiewicz #include <asm/unaligned.h>
2900a2430fSAndrzej Pietrasiewicz
307f7c548cSVincent Pelletier #include <linux/usb/ccid.h>
3100a2430fSAndrzej Pietrasiewicz #include <linux/usb/composite.h>
3200a2430fSAndrzej Pietrasiewicz #include <linux/usb/functionfs.h>
3300a2430fSAndrzej Pietrasiewicz
3400a2430fSAndrzej Pietrasiewicz #include <linux/aio.h>
359bf5b9ebSChristoph Hellwig #include <linux/kthread.h>
3600a2430fSAndrzej Pietrasiewicz #include <linux/poll.h>
375e33f6fdSRobert Baldyga #include <linux/eventfd.h>
3800a2430fSAndrzej Pietrasiewicz
3900a2430fSAndrzej Pietrasiewicz #include "u_fs.h"
4000a2430fSAndrzej Pietrasiewicz #include "u_f.h"
4100a2430fSAndrzej Pietrasiewicz #include "u_os_desc.h"
4200a2430fSAndrzej Pietrasiewicz #include "configfs.h"
4300a2430fSAndrzej Pietrasiewicz
4400a2430fSAndrzej Pietrasiewicz #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
4500a2430fSAndrzej Pietrasiewicz
4600a2430fSAndrzej Pietrasiewicz /* Reference counter handling */
4700a2430fSAndrzej Pietrasiewicz static void ffs_data_get(struct ffs_data *ffs);
4800a2430fSAndrzej Pietrasiewicz static void ffs_data_put(struct ffs_data *ffs);
4900a2430fSAndrzej Pietrasiewicz /* Creates new ffs_data object. */
50addfc582SJohn Keeping static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
51addfc582SJohn Keeping __attribute__((malloc));
5200a2430fSAndrzej Pietrasiewicz
5300a2430fSAndrzej Pietrasiewicz /* Opened counter handling. */
5400a2430fSAndrzej Pietrasiewicz static void ffs_data_opened(struct ffs_data *ffs);
5500a2430fSAndrzej Pietrasiewicz static void ffs_data_closed(struct ffs_data *ffs);
5600a2430fSAndrzej Pietrasiewicz
5700a2430fSAndrzej Pietrasiewicz /* Called with ffs->mutex held; take over ownership of data. */
5800a2430fSAndrzej Pietrasiewicz static int __must_check
5900a2430fSAndrzej Pietrasiewicz __ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
6000a2430fSAndrzej Pietrasiewicz static int __must_check
6100a2430fSAndrzej Pietrasiewicz __ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
6200a2430fSAndrzej Pietrasiewicz
6300a2430fSAndrzej Pietrasiewicz
6400a2430fSAndrzej Pietrasiewicz /* The function structure ***************************************************/
6500a2430fSAndrzej Pietrasiewicz
6600a2430fSAndrzej Pietrasiewicz struct ffs_ep;
6700a2430fSAndrzej Pietrasiewicz
6800a2430fSAndrzej Pietrasiewicz struct ffs_function {
6900a2430fSAndrzej Pietrasiewicz struct usb_configuration *conf;
7000a2430fSAndrzej Pietrasiewicz struct usb_gadget *gadget;
7100a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs;
7200a2430fSAndrzej Pietrasiewicz
7300a2430fSAndrzej Pietrasiewicz struct ffs_ep *eps;
7400a2430fSAndrzej Pietrasiewicz u8 eps_revmap[16];
7500a2430fSAndrzej Pietrasiewicz short *interfaces_nums;
7600a2430fSAndrzej Pietrasiewicz
7700a2430fSAndrzej Pietrasiewicz struct usb_function function;
7800a2430fSAndrzej Pietrasiewicz };
7900a2430fSAndrzej Pietrasiewicz
8000a2430fSAndrzej Pietrasiewicz
ffs_func_from_usb(struct usb_function * f)8100a2430fSAndrzej Pietrasiewicz static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
8200a2430fSAndrzej Pietrasiewicz {
8300a2430fSAndrzej Pietrasiewicz return container_of(f, struct ffs_function, function);
8400a2430fSAndrzej Pietrasiewicz }
8500a2430fSAndrzej Pietrasiewicz
8600a2430fSAndrzej Pietrasiewicz
8700a2430fSAndrzej Pietrasiewicz static inline enum ffs_setup_state
ffs_setup_state_clear_cancelled(struct ffs_data * ffs)8800a2430fSAndrzej Pietrasiewicz ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
8900a2430fSAndrzej Pietrasiewicz {
9000a2430fSAndrzej Pietrasiewicz return (enum ffs_setup_state)
9100a2430fSAndrzej Pietrasiewicz cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
9200a2430fSAndrzej Pietrasiewicz }
9300a2430fSAndrzej Pietrasiewicz
9400a2430fSAndrzej Pietrasiewicz
9500a2430fSAndrzej Pietrasiewicz static void ffs_func_eps_disable(struct ffs_function *func);
9600a2430fSAndrzej Pietrasiewicz static int __must_check ffs_func_eps_enable(struct ffs_function *func);
9700a2430fSAndrzej Pietrasiewicz
9800a2430fSAndrzej Pietrasiewicz static int ffs_func_bind(struct usb_configuration *,
9900a2430fSAndrzej Pietrasiewicz struct usb_function *);
10000a2430fSAndrzej Pietrasiewicz static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
10100a2430fSAndrzej Pietrasiewicz static void ffs_func_disable(struct usb_function *);
10200a2430fSAndrzej Pietrasiewicz static int ffs_func_setup(struct usb_function *,
10300a2430fSAndrzej Pietrasiewicz const struct usb_ctrlrequest *);
10454dfce6dSFelix Hädicke static bool ffs_func_req_match(struct usb_function *,
1051a00b457SFelix Hädicke const struct usb_ctrlrequest *,
1061a00b457SFelix Hädicke bool config0);
10700a2430fSAndrzej Pietrasiewicz static void ffs_func_suspend(struct usb_function *);
10800a2430fSAndrzej Pietrasiewicz static void ffs_func_resume(struct usb_function *);
10900a2430fSAndrzej Pietrasiewicz
11000a2430fSAndrzej Pietrasiewicz
11100a2430fSAndrzej Pietrasiewicz static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
11200a2430fSAndrzej Pietrasiewicz static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
11300a2430fSAndrzej Pietrasiewicz
11400a2430fSAndrzej Pietrasiewicz
11500a2430fSAndrzej Pietrasiewicz /* The endpoints structures *************************************************/
11600a2430fSAndrzej Pietrasiewicz
11700a2430fSAndrzej Pietrasiewicz struct ffs_ep {
11800a2430fSAndrzej Pietrasiewicz struct usb_ep *ep; /* P: ffs->eps_lock */
11900a2430fSAndrzej Pietrasiewicz struct usb_request *req; /* P: epfile->mutex */
12000a2430fSAndrzej Pietrasiewicz
12100a2430fSAndrzej Pietrasiewicz /* [0]: full speed, [1]: high speed, [2]: super speed */
12200a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor *descs[3];
12300a2430fSAndrzej Pietrasiewicz
12400a2430fSAndrzej Pietrasiewicz u8 num;
12500a2430fSAndrzej Pietrasiewicz };
12600a2430fSAndrzej Pietrasiewicz
12700a2430fSAndrzej Pietrasiewicz struct ffs_epfile {
12800a2430fSAndrzej Pietrasiewicz /* Protects ep->ep and ep->req. */
12900a2430fSAndrzej Pietrasiewicz struct mutex mutex;
13000a2430fSAndrzej Pietrasiewicz
13100a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs;
13200a2430fSAndrzej Pietrasiewicz struct ffs_ep *ep; /* P: ffs->eps_lock */
13300a2430fSAndrzej Pietrasiewicz
13400a2430fSAndrzej Pietrasiewicz struct dentry *dentry;
13500a2430fSAndrzej Pietrasiewicz
1369353afbbSMichal Nazarewicz /*
1379353afbbSMichal Nazarewicz * Buffer for holding data from partial reads which may happen since
1389353afbbSMichal Nazarewicz * we’re rounding user read requests to a multiple of a max packet size.
139a9e6f83cSMichal Nazarewicz *
140a9e6f83cSMichal Nazarewicz * The pointer is initialised with NULL value and may be set by
141a9e6f83cSMichal Nazarewicz * __ffs_epfile_read_data function to point to a temporary buffer.
142a9e6f83cSMichal Nazarewicz *
143a9e6f83cSMichal Nazarewicz * In normal operation, calls to __ffs_epfile_read_buffered will consume
144a9e6f83cSMichal Nazarewicz * data from said buffer and eventually free it. Importantly, while the
145a9e6f83cSMichal Nazarewicz * function is using the buffer, it sets the pointer to NULL. This is
146a9e6f83cSMichal Nazarewicz * all right since __ffs_epfile_read_data and __ffs_epfile_read_buffered
147a9e6f83cSMichal Nazarewicz * can never run concurrently (they are synchronised by epfile->mutex)
148a9e6f83cSMichal Nazarewicz * so the latter will not assign a new value to the pointer.
149a9e6f83cSMichal Nazarewicz *
150a9e6f83cSMichal Nazarewicz * Meanwhile ffs_func_eps_disable frees the buffer (if the pointer is
151a9e6f83cSMichal Nazarewicz * valid) and sets the pointer to READ_BUFFER_DROP value. This special
152a9e6f83cSMichal Nazarewicz * value is crux of the synchronisation between ffs_func_eps_disable and
153a9e6f83cSMichal Nazarewicz * __ffs_epfile_read_data.
154a9e6f83cSMichal Nazarewicz *
155a9e6f83cSMichal Nazarewicz * Once __ffs_epfile_read_data is about to finish it will try to set the
156a9e6f83cSMichal Nazarewicz * pointer back to its old value (as described above), but seeing as the
157a9e6f83cSMichal Nazarewicz * pointer is not-NULL (namely READ_BUFFER_DROP) it will instead free
158a9e6f83cSMichal Nazarewicz * the buffer.
159a9e6f83cSMichal Nazarewicz *
160a9e6f83cSMichal Nazarewicz * == State transitions ==
161a9e6f83cSMichal Nazarewicz *
162a9e6f83cSMichal Nazarewicz * • ptr == NULL: (initial state)
163a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP
164a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffered: nop
165a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_data allocates temp buffer: go to ptr == buf
166a9e6f83cSMichal Nazarewicz * ◦ reading finishes: n/a, not in ‘and reading’ state
167a9e6f83cSMichal Nazarewicz * • ptr == DROP:
168a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffer_free: nop
169a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffered: go to ptr == NULL
170a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_data allocates temp buffer: free buf, nop
171a9e6f83cSMichal Nazarewicz * ◦ reading finishes: n/a, not in ‘and reading’ state
172a9e6f83cSMichal Nazarewicz * • ptr == buf:
173a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffer_free: free buf, go to ptr == DROP
174a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffered: go to ptr == NULL and reading
175a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_data: n/a, __ffs_epfile_read_buffered
176a9e6f83cSMichal Nazarewicz * is always called first
177a9e6f83cSMichal Nazarewicz * ◦ reading finishes: n/a, not in ‘and reading’ state
178a9e6f83cSMichal Nazarewicz * • ptr == NULL and reading:
179a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP and reading
180a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffered: n/a, mutex is held
181a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_data: n/a, mutex is held
182a9e6f83cSMichal Nazarewicz * ◦ reading finishes and …
183a9e6f83cSMichal Nazarewicz * … all data read: free buf, go to ptr == NULL
184a9e6f83cSMichal Nazarewicz * … otherwise: go to ptr == buf and reading
185a9e6f83cSMichal Nazarewicz * • ptr == DROP and reading:
186a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffer_free: nop
187a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_buffered: n/a, mutex is held
188a9e6f83cSMichal Nazarewicz * ◦ __ffs_epfile_read_data: n/a, mutex is held
189a9e6f83cSMichal Nazarewicz * ◦ reading finishes: free buf, go to ptr == DROP
1909353afbbSMichal Nazarewicz */
191a9e6f83cSMichal Nazarewicz struct ffs_buffer *read_buffer;
192a9e6f83cSMichal Nazarewicz #define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
1939353afbbSMichal Nazarewicz
19400a2430fSAndrzej Pietrasiewicz char name[5];
19500a2430fSAndrzej Pietrasiewicz
19600a2430fSAndrzej Pietrasiewicz unsigned char in; /* P: ffs->eps_lock */
19700a2430fSAndrzej Pietrasiewicz unsigned char isoc; /* P: ffs->eps_lock */
19800a2430fSAndrzej Pietrasiewicz
19900a2430fSAndrzej Pietrasiewicz unsigned char _pad;
20000a2430fSAndrzej Pietrasiewicz };
20100a2430fSAndrzej Pietrasiewicz
2029353afbbSMichal Nazarewicz struct ffs_buffer {
2039353afbbSMichal Nazarewicz size_t length;
2049353afbbSMichal Nazarewicz char *data;
2059353afbbSMichal Nazarewicz char storage[];
2069353afbbSMichal Nazarewicz };
2079353afbbSMichal Nazarewicz
20800a2430fSAndrzej Pietrasiewicz /* ffs_io_data structure ***************************************************/
20900a2430fSAndrzej Pietrasiewicz
21000a2430fSAndrzej Pietrasiewicz struct ffs_io_data {
21100a2430fSAndrzej Pietrasiewicz bool aio;
21200a2430fSAndrzej Pietrasiewicz bool read;
21300a2430fSAndrzej Pietrasiewicz
21400a2430fSAndrzej Pietrasiewicz struct kiocb *kiocb;
215c993c39bSAl Viro struct iov_iter data;
216c993c39bSAl Viro const void *to_free;
217c993c39bSAl Viro char *buf;
21800a2430fSAndrzej Pietrasiewicz
21900a2430fSAndrzej Pietrasiewicz struct mm_struct *mm;
22000a2430fSAndrzej Pietrasiewicz struct work_struct work;
22100a2430fSAndrzej Pietrasiewicz
22200a2430fSAndrzej Pietrasiewicz struct usb_ep *ep;
22300a2430fSAndrzej Pietrasiewicz struct usb_request *req;
224772a7a72SAndrzej Pietrasiewicz struct sg_table sgt;
225772a7a72SAndrzej Pietrasiewicz bool use_sg;
2265e33f6fdSRobert Baldyga
2275e33f6fdSRobert Baldyga struct ffs_data *ffs;
228fb1f16d7SLinyu Yuan
229fb1f16d7SLinyu Yuan int status;
230fb1f16d7SLinyu Yuan struct completion done;
23100a2430fSAndrzej Pietrasiewicz };
23200a2430fSAndrzej Pietrasiewicz
2336d5c1c77SRobert Baldyga struct ffs_desc_helper {
2346d5c1c77SRobert Baldyga struct ffs_data *ffs;
2356d5c1c77SRobert Baldyga unsigned interfaces_count;
2366d5c1c77SRobert Baldyga unsigned eps_count;
2376d5c1c77SRobert Baldyga };
2386d5c1c77SRobert Baldyga
23900a2430fSAndrzej Pietrasiewicz static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
24000a2430fSAndrzej Pietrasiewicz static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
24100a2430fSAndrzej Pietrasiewicz
2421bb27cacSAl Viro static struct dentry *
24300a2430fSAndrzej Pietrasiewicz ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
2441bb27cacSAl Viro const struct file_operations *fops);
24500a2430fSAndrzej Pietrasiewicz
24600a2430fSAndrzej Pietrasiewicz /* Devices management *******************************************************/
24700a2430fSAndrzej Pietrasiewicz
24800a2430fSAndrzej Pietrasiewicz DEFINE_MUTEX(ffs_lock);
24900a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(ffs_lock);
25000a2430fSAndrzej Pietrasiewicz
25100a2430fSAndrzej Pietrasiewicz static struct ffs_dev *_ffs_find_dev(const char *name);
25200a2430fSAndrzej Pietrasiewicz static struct ffs_dev *_ffs_alloc_dev(void);
25300a2430fSAndrzej Pietrasiewicz static void _ffs_free_dev(struct ffs_dev *dev);
254ecfbd7b9SAndrew Gabbasov static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
255ecfbd7b9SAndrew Gabbasov static void ffs_release_dev(struct ffs_dev *ffs_dev);
25600a2430fSAndrzej Pietrasiewicz static int ffs_ready(struct ffs_data *ffs);
25700a2430fSAndrzej Pietrasiewicz static void ffs_closed(struct ffs_data *ffs);
25800a2430fSAndrzej Pietrasiewicz
25900a2430fSAndrzej Pietrasiewicz /* Misc helper functions ****************************************************/
26000a2430fSAndrzej Pietrasiewicz
26100a2430fSAndrzej Pietrasiewicz static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
26200a2430fSAndrzej Pietrasiewicz __attribute__((warn_unused_result, nonnull));
26300a2430fSAndrzej Pietrasiewicz static char *ffs_prepare_buffer(const char __user *buf, size_t len)
26400a2430fSAndrzej Pietrasiewicz __attribute__((warn_unused_result, nonnull));
26500a2430fSAndrzej Pietrasiewicz
26600a2430fSAndrzej Pietrasiewicz
26700a2430fSAndrzej Pietrasiewicz /* Control file aka ep0 *****************************************************/
26800a2430fSAndrzej Pietrasiewicz
ffs_ep0_complete(struct usb_ep * ep,struct usb_request * req)26900a2430fSAndrzej Pietrasiewicz static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
27000a2430fSAndrzej Pietrasiewicz {
27100a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = req->context;
27200a2430fSAndrzej Pietrasiewicz
2735bdcde90SDaniel Wagner complete(&ffs->ep0req_completion);
27400a2430fSAndrzej Pietrasiewicz }
27500a2430fSAndrzej Pietrasiewicz
__ffs_ep0_queue_wait(struct ffs_data * ffs,char * data,size_t len)27600a2430fSAndrzej Pietrasiewicz static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
277c40619bbSVincent Pelletier __releases(&ffs->ev.waitq.lock)
27800a2430fSAndrzej Pietrasiewicz {
27900a2430fSAndrzej Pietrasiewicz struct usb_request *req = ffs->ep0req;
28000a2430fSAndrzej Pietrasiewicz int ret;
28100a2430fSAndrzej Pietrasiewicz
282921deb9dSUdipto Goswami if (!req) {
283921deb9dSUdipto Goswami spin_unlock_irq(&ffs->ev.waitq.lock);
2846a19da11SUdipto Goswami return -EINVAL;
285921deb9dSUdipto Goswami }
2866a19da11SUdipto Goswami
28700a2430fSAndrzej Pietrasiewicz req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
28800a2430fSAndrzej Pietrasiewicz
28900a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
29000a2430fSAndrzej Pietrasiewicz
29100a2430fSAndrzej Pietrasiewicz req->buf = data;
29200a2430fSAndrzej Pietrasiewicz req->length = len;
29300a2430fSAndrzej Pietrasiewicz
29400a2430fSAndrzej Pietrasiewicz /*
29500a2430fSAndrzej Pietrasiewicz * UDC layer requires to provide a buffer even for ZLP, but should
29600a2430fSAndrzej Pietrasiewicz * not use it at all. Let's provide some poisoned pointer to catch
29700a2430fSAndrzej Pietrasiewicz * possible bug in the driver.
29800a2430fSAndrzej Pietrasiewicz */
29900a2430fSAndrzej Pietrasiewicz if (req->buf == NULL)
30000a2430fSAndrzej Pietrasiewicz req->buf = (void *)0xDEADBABE;
30100a2430fSAndrzej Pietrasiewicz
30200a2430fSAndrzej Pietrasiewicz reinit_completion(&ffs->ep0req_completion);
30300a2430fSAndrzej Pietrasiewicz
30400a2430fSAndrzej Pietrasiewicz ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
3058704fd73SGreg Kroah-Hartman if (ret < 0)
30600a2430fSAndrzej Pietrasiewicz return ret;
30700a2430fSAndrzej Pietrasiewicz
30800a2430fSAndrzej Pietrasiewicz ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
3098704fd73SGreg Kroah-Hartman if (ret) {
31000a2430fSAndrzej Pietrasiewicz usb_ep_dequeue(ffs->gadget->ep0, req);
31100a2430fSAndrzej Pietrasiewicz return -EINTR;
31200a2430fSAndrzej Pietrasiewicz }
31300a2430fSAndrzej Pietrasiewicz
31400a2430fSAndrzej Pietrasiewicz ffs->setup_state = FFS_NO_SETUP;
31500a2430fSAndrzej Pietrasiewicz return req->status ? req->status : req->actual;
31600a2430fSAndrzej Pietrasiewicz }
31700a2430fSAndrzej Pietrasiewicz
__ffs_ep0_stall(struct ffs_data * ffs)31800a2430fSAndrzej Pietrasiewicz static int __ffs_ep0_stall(struct ffs_data *ffs)
31900a2430fSAndrzej Pietrasiewicz {
32000a2430fSAndrzej Pietrasiewicz if (ffs->ev.can_stall) {
32100a2430fSAndrzej Pietrasiewicz pr_vdebug("ep0 stall\n");
32200a2430fSAndrzej Pietrasiewicz usb_ep_set_halt(ffs->gadget->ep0);
32300a2430fSAndrzej Pietrasiewicz ffs->setup_state = FFS_NO_SETUP;
32400a2430fSAndrzej Pietrasiewicz return -EL2HLT;
32500a2430fSAndrzej Pietrasiewicz } else {
32600a2430fSAndrzej Pietrasiewicz pr_debug("bogus ep0 stall!\n");
32700a2430fSAndrzej Pietrasiewicz return -ESRCH;
32800a2430fSAndrzej Pietrasiewicz }
32900a2430fSAndrzej Pietrasiewicz }
33000a2430fSAndrzej Pietrasiewicz
ffs_ep0_write(struct file * file,const char __user * buf,size_t len,loff_t * ptr)33100a2430fSAndrzej Pietrasiewicz static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
33200a2430fSAndrzej Pietrasiewicz size_t len, loff_t *ptr)
33300a2430fSAndrzej Pietrasiewicz {
33400a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = file->private_data;
33500a2430fSAndrzej Pietrasiewicz ssize_t ret;
33600a2430fSAndrzej Pietrasiewicz char *data;
33700a2430fSAndrzej Pietrasiewicz
33800a2430fSAndrzej Pietrasiewicz /* Fast check if setup was canceled */
33900a2430fSAndrzej Pietrasiewicz if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
34000a2430fSAndrzej Pietrasiewicz return -EIDRM;
34100a2430fSAndrzej Pietrasiewicz
34200a2430fSAndrzej Pietrasiewicz /* Acquire mutex */
34300a2430fSAndrzej Pietrasiewicz ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
3448704fd73SGreg Kroah-Hartman if (ret < 0)
34500a2430fSAndrzej Pietrasiewicz return ret;
34600a2430fSAndrzej Pietrasiewicz
34700a2430fSAndrzej Pietrasiewicz /* Check state */
34800a2430fSAndrzej Pietrasiewicz switch (ffs->state) {
34900a2430fSAndrzej Pietrasiewicz case FFS_READ_DESCRIPTORS:
35000a2430fSAndrzej Pietrasiewicz case FFS_READ_STRINGS:
35100a2430fSAndrzej Pietrasiewicz /* Copy data */
3528704fd73SGreg Kroah-Hartman if (len < 16) {
35300a2430fSAndrzej Pietrasiewicz ret = -EINVAL;
35400a2430fSAndrzej Pietrasiewicz break;
35500a2430fSAndrzej Pietrasiewicz }
35600a2430fSAndrzej Pietrasiewicz
35700a2430fSAndrzej Pietrasiewicz data = ffs_prepare_buffer(buf, len);
35800a2430fSAndrzej Pietrasiewicz if (IS_ERR(data)) {
35900a2430fSAndrzej Pietrasiewicz ret = PTR_ERR(data);
36000a2430fSAndrzej Pietrasiewicz break;
36100a2430fSAndrzej Pietrasiewicz }
36200a2430fSAndrzej Pietrasiewicz
36300a2430fSAndrzej Pietrasiewicz /* Handle data */
36400a2430fSAndrzej Pietrasiewicz if (ffs->state == FFS_READ_DESCRIPTORS) {
36500a2430fSAndrzej Pietrasiewicz pr_info("read descriptors\n");
36600a2430fSAndrzej Pietrasiewicz ret = __ffs_data_got_descs(ffs, data, len);
3678704fd73SGreg Kroah-Hartman if (ret < 0)
36800a2430fSAndrzej Pietrasiewicz break;
36900a2430fSAndrzej Pietrasiewicz
37000a2430fSAndrzej Pietrasiewicz ffs->state = FFS_READ_STRINGS;
37100a2430fSAndrzej Pietrasiewicz ret = len;
37200a2430fSAndrzej Pietrasiewicz } else {
37300a2430fSAndrzej Pietrasiewicz pr_info("read strings\n");
37400a2430fSAndrzej Pietrasiewicz ret = __ffs_data_got_strings(ffs, data, len);
3758704fd73SGreg Kroah-Hartman if (ret < 0)
37600a2430fSAndrzej Pietrasiewicz break;
37700a2430fSAndrzej Pietrasiewicz
37800a2430fSAndrzej Pietrasiewicz ret = ffs_epfiles_create(ffs);
3798704fd73SGreg Kroah-Hartman if (ret) {
38000a2430fSAndrzej Pietrasiewicz ffs->state = FFS_CLOSING;
38100a2430fSAndrzej Pietrasiewicz break;
38200a2430fSAndrzej Pietrasiewicz }
38300a2430fSAndrzej Pietrasiewicz
38400a2430fSAndrzej Pietrasiewicz ffs->state = FFS_ACTIVE;
38500a2430fSAndrzej Pietrasiewicz mutex_unlock(&ffs->mutex);
38600a2430fSAndrzej Pietrasiewicz
38700a2430fSAndrzej Pietrasiewicz ret = ffs_ready(ffs);
3888704fd73SGreg Kroah-Hartman if (ret < 0) {
38900a2430fSAndrzej Pietrasiewicz ffs->state = FFS_CLOSING;
39000a2430fSAndrzej Pietrasiewicz return ret;
39100a2430fSAndrzej Pietrasiewicz }
39200a2430fSAndrzej Pietrasiewicz
39300a2430fSAndrzej Pietrasiewicz return len;
39400a2430fSAndrzej Pietrasiewicz }
39500a2430fSAndrzej Pietrasiewicz break;
39600a2430fSAndrzej Pietrasiewicz
39700a2430fSAndrzej Pietrasiewicz case FFS_ACTIVE:
39800a2430fSAndrzej Pietrasiewicz data = NULL;
39900a2430fSAndrzej Pietrasiewicz /*
40000a2430fSAndrzej Pietrasiewicz * We're called from user space, we can use _irq
40100a2430fSAndrzej Pietrasiewicz * rather then _irqsave
40200a2430fSAndrzej Pietrasiewicz */
40300a2430fSAndrzej Pietrasiewicz spin_lock_irq(&ffs->ev.waitq.lock);
40400a2430fSAndrzej Pietrasiewicz switch (ffs_setup_state_clear_cancelled(ffs)) {
40500a2430fSAndrzej Pietrasiewicz case FFS_SETUP_CANCELLED:
40600a2430fSAndrzej Pietrasiewicz ret = -EIDRM;
40700a2430fSAndrzej Pietrasiewicz goto done_spin;
40800a2430fSAndrzej Pietrasiewicz
40900a2430fSAndrzej Pietrasiewicz case FFS_NO_SETUP:
41000a2430fSAndrzej Pietrasiewicz ret = -ESRCH;
41100a2430fSAndrzej Pietrasiewicz goto done_spin;
41200a2430fSAndrzej Pietrasiewicz
41300a2430fSAndrzej Pietrasiewicz case FFS_SETUP_PENDING:
41400a2430fSAndrzej Pietrasiewicz break;
41500a2430fSAndrzej Pietrasiewicz }
41600a2430fSAndrzej Pietrasiewicz
41700a2430fSAndrzej Pietrasiewicz /* FFS_SETUP_PENDING */
41800a2430fSAndrzej Pietrasiewicz if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
41900a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
42000a2430fSAndrzej Pietrasiewicz ret = __ffs_ep0_stall(ffs);
42100a2430fSAndrzej Pietrasiewicz break;
42200a2430fSAndrzej Pietrasiewicz }
42300a2430fSAndrzej Pietrasiewicz
42400a2430fSAndrzej Pietrasiewicz /* FFS_SETUP_PENDING and not stall */
42500a2430fSAndrzej Pietrasiewicz len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
42600a2430fSAndrzej Pietrasiewicz
42700a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
42800a2430fSAndrzej Pietrasiewicz
42900a2430fSAndrzej Pietrasiewicz data = ffs_prepare_buffer(buf, len);
43000a2430fSAndrzej Pietrasiewicz if (IS_ERR(data)) {
43100a2430fSAndrzej Pietrasiewicz ret = PTR_ERR(data);
43200a2430fSAndrzej Pietrasiewicz break;
43300a2430fSAndrzej Pietrasiewicz }
43400a2430fSAndrzej Pietrasiewicz
43500a2430fSAndrzej Pietrasiewicz spin_lock_irq(&ffs->ev.waitq.lock);
43600a2430fSAndrzej Pietrasiewicz
43700a2430fSAndrzej Pietrasiewicz /*
43800a2430fSAndrzej Pietrasiewicz * We are guaranteed to be still in FFS_ACTIVE state
43900a2430fSAndrzej Pietrasiewicz * but the state of setup could have changed from
44000a2430fSAndrzej Pietrasiewicz * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
44100a2430fSAndrzej Pietrasiewicz * to check for that. If that happened we copied data
44200a2430fSAndrzej Pietrasiewicz * from user space in vain but it's unlikely.
44300a2430fSAndrzej Pietrasiewicz *
44400a2430fSAndrzej Pietrasiewicz * For sure we are not in FFS_NO_SETUP since this is
44500a2430fSAndrzej Pietrasiewicz * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
44600a2430fSAndrzej Pietrasiewicz * transition can be performed and it's protected by
44700a2430fSAndrzej Pietrasiewicz * mutex.
44800a2430fSAndrzej Pietrasiewicz */
44900a2430fSAndrzej Pietrasiewicz if (ffs_setup_state_clear_cancelled(ffs) ==
45000a2430fSAndrzej Pietrasiewicz FFS_SETUP_CANCELLED) {
45100a2430fSAndrzej Pietrasiewicz ret = -EIDRM;
45200a2430fSAndrzej Pietrasiewicz done_spin:
45300a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
45400a2430fSAndrzej Pietrasiewicz } else {
45500a2430fSAndrzej Pietrasiewicz /* unlocks spinlock */
45600a2430fSAndrzej Pietrasiewicz ret = __ffs_ep0_queue_wait(ffs, data, len);
45700a2430fSAndrzej Pietrasiewicz }
45800a2430fSAndrzej Pietrasiewicz kfree(data);
45900a2430fSAndrzej Pietrasiewicz break;
46000a2430fSAndrzej Pietrasiewicz
46100a2430fSAndrzej Pietrasiewicz default:
46200a2430fSAndrzej Pietrasiewicz ret = -EBADFD;
46300a2430fSAndrzej Pietrasiewicz break;
46400a2430fSAndrzej Pietrasiewicz }
46500a2430fSAndrzej Pietrasiewicz
46600a2430fSAndrzej Pietrasiewicz mutex_unlock(&ffs->mutex);
46700a2430fSAndrzej Pietrasiewicz return ret;
46800a2430fSAndrzej Pietrasiewicz }
46900a2430fSAndrzej Pietrasiewicz
47067913bbdSMichal Nazarewicz /* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
__ffs_ep0_read_events(struct ffs_data * ffs,char __user * buf,size_t n)47100a2430fSAndrzej Pietrasiewicz static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
47200a2430fSAndrzej Pietrasiewicz size_t n)
473c40619bbSVincent Pelletier __releases(&ffs->ev.waitq.lock)
47400a2430fSAndrzej Pietrasiewicz {
47500a2430fSAndrzej Pietrasiewicz /*
47667913bbdSMichal Nazarewicz * n cannot be bigger than ffs->ev.count, which cannot be bigger than
47767913bbdSMichal Nazarewicz * size of ffs->ev.types array (which is four) so that's how much space
47867913bbdSMichal Nazarewicz * we reserve.
47900a2430fSAndrzej Pietrasiewicz */
48067913bbdSMichal Nazarewicz struct usb_functionfs_event events[ARRAY_SIZE(ffs->ev.types)];
48167913bbdSMichal Nazarewicz const size_t size = n * sizeof *events;
48200a2430fSAndrzej Pietrasiewicz unsigned i = 0;
48300a2430fSAndrzej Pietrasiewicz
48467913bbdSMichal Nazarewicz memset(events, 0, size);
48500a2430fSAndrzej Pietrasiewicz
48600a2430fSAndrzej Pietrasiewicz do {
48700a2430fSAndrzej Pietrasiewicz events[i].type = ffs->ev.types[i];
48800a2430fSAndrzej Pietrasiewicz if (events[i].type == FUNCTIONFS_SETUP) {
48900a2430fSAndrzej Pietrasiewicz events[i].u.setup = ffs->ev.setup;
49000a2430fSAndrzej Pietrasiewicz ffs->setup_state = FFS_SETUP_PENDING;
49100a2430fSAndrzej Pietrasiewicz }
49200a2430fSAndrzej Pietrasiewicz } while (++i < n);
49300a2430fSAndrzej Pietrasiewicz
49400a2430fSAndrzej Pietrasiewicz ffs->ev.count -= n;
49567913bbdSMichal Nazarewicz if (ffs->ev.count)
49600a2430fSAndrzej Pietrasiewicz memmove(ffs->ev.types, ffs->ev.types + n,
49700a2430fSAndrzej Pietrasiewicz ffs->ev.count * sizeof *ffs->ev.types);
49800a2430fSAndrzej Pietrasiewicz
49900a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
50000a2430fSAndrzej Pietrasiewicz mutex_unlock(&ffs->mutex);
50100a2430fSAndrzej Pietrasiewicz
5028704fd73SGreg Kroah-Hartman return copy_to_user(buf, events, size) ? -EFAULT : size;
50300a2430fSAndrzej Pietrasiewicz }
50400a2430fSAndrzej Pietrasiewicz
ffs_ep0_read(struct file * file,char __user * buf,size_t len,loff_t * ptr)50500a2430fSAndrzej Pietrasiewicz static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
50600a2430fSAndrzej Pietrasiewicz size_t len, loff_t *ptr)
50700a2430fSAndrzej Pietrasiewicz {
50800a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = file->private_data;
50900a2430fSAndrzej Pietrasiewicz char *data = NULL;
51000a2430fSAndrzej Pietrasiewicz size_t n;
51100a2430fSAndrzej Pietrasiewicz int ret;
51200a2430fSAndrzej Pietrasiewicz
51300a2430fSAndrzej Pietrasiewicz /* Fast check if setup was canceled */
51400a2430fSAndrzej Pietrasiewicz if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
51500a2430fSAndrzej Pietrasiewicz return -EIDRM;
51600a2430fSAndrzej Pietrasiewicz
51700a2430fSAndrzej Pietrasiewicz /* Acquire mutex */
51800a2430fSAndrzej Pietrasiewicz ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
5198704fd73SGreg Kroah-Hartman if (ret < 0)
52000a2430fSAndrzej Pietrasiewicz return ret;
52100a2430fSAndrzej Pietrasiewicz
52200a2430fSAndrzej Pietrasiewicz /* Check state */
52300a2430fSAndrzej Pietrasiewicz if (ffs->state != FFS_ACTIVE) {
52400a2430fSAndrzej Pietrasiewicz ret = -EBADFD;
52500a2430fSAndrzej Pietrasiewicz goto done_mutex;
52600a2430fSAndrzej Pietrasiewicz }
52700a2430fSAndrzej Pietrasiewicz
52800a2430fSAndrzej Pietrasiewicz /*
52900a2430fSAndrzej Pietrasiewicz * We're called from user space, we can use _irq rather then
53000a2430fSAndrzej Pietrasiewicz * _irqsave
53100a2430fSAndrzej Pietrasiewicz */
53200a2430fSAndrzej Pietrasiewicz spin_lock_irq(&ffs->ev.waitq.lock);
53300a2430fSAndrzej Pietrasiewicz
53400a2430fSAndrzej Pietrasiewicz switch (ffs_setup_state_clear_cancelled(ffs)) {
53500a2430fSAndrzej Pietrasiewicz case FFS_SETUP_CANCELLED:
53600a2430fSAndrzej Pietrasiewicz ret = -EIDRM;
53700a2430fSAndrzej Pietrasiewicz break;
53800a2430fSAndrzej Pietrasiewicz
53900a2430fSAndrzej Pietrasiewicz case FFS_NO_SETUP:
54000a2430fSAndrzej Pietrasiewicz n = len / sizeof(struct usb_functionfs_event);
5418704fd73SGreg Kroah-Hartman if (!n) {
54200a2430fSAndrzej Pietrasiewicz ret = -EINVAL;
54300a2430fSAndrzej Pietrasiewicz break;
54400a2430fSAndrzej Pietrasiewicz }
54500a2430fSAndrzej Pietrasiewicz
54600a2430fSAndrzej Pietrasiewicz if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
54700a2430fSAndrzej Pietrasiewicz ret = -EAGAIN;
54800a2430fSAndrzej Pietrasiewicz break;
54900a2430fSAndrzej Pietrasiewicz }
55000a2430fSAndrzej Pietrasiewicz
55100a2430fSAndrzej Pietrasiewicz if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
55200a2430fSAndrzej Pietrasiewicz ffs->ev.count)) {
55300a2430fSAndrzej Pietrasiewicz ret = -EINTR;
55400a2430fSAndrzej Pietrasiewicz break;
55500a2430fSAndrzej Pietrasiewicz }
55600a2430fSAndrzej Pietrasiewicz
557c40619bbSVincent Pelletier /* unlocks spinlock */
55800a2430fSAndrzej Pietrasiewicz return __ffs_ep0_read_events(ffs, buf,
55900a2430fSAndrzej Pietrasiewicz min(n, (size_t)ffs->ev.count));
56000a2430fSAndrzej Pietrasiewicz
56100a2430fSAndrzej Pietrasiewicz case FFS_SETUP_PENDING:
56200a2430fSAndrzej Pietrasiewicz if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
56300a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
56400a2430fSAndrzej Pietrasiewicz ret = __ffs_ep0_stall(ffs);
56500a2430fSAndrzej Pietrasiewicz goto done_mutex;
56600a2430fSAndrzej Pietrasiewicz }
56700a2430fSAndrzej Pietrasiewicz
56800a2430fSAndrzej Pietrasiewicz len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
56900a2430fSAndrzej Pietrasiewicz
57000a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
57100a2430fSAndrzej Pietrasiewicz
5728704fd73SGreg Kroah-Hartman if (len) {
57300a2430fSAndrzej Pietrasiewicz data = kmalloc(len, GFP_KERNEL);
5748704fd73SGreg Kroah-Hartman if (!data) {
57500a2430fSAndrzej Pietrasiewicz ret = -ENOMEM;
57600a2430fSAndrzej Pietrasiewicz goto done_mutex;
57700a2430fSAndrzej Pietrasiewicz }
57800a2430fSAndrzej Pietrasiewicz }
57900a2430fSAndrzej Pietrasiewicz
58000a2430fSAndrzej Pietrasiewicz spin_lock_irq(&ffs->ev.waitq.lock);
58100a2430fSAndrzej Pietrasiewicz
58200a2430fSAndrzej Pietrasiewicz /* See ffs_ep0_write() */
58300a2430fSAndrzej Pietrasiewicz if (ffs_setup_state_clear_cancelled(ffs) ==
58400a2430fSAndrzej Pietrasiewicz FFS_SETUP_CANCELLED) {
58500a2430fSAndrzej Pietrasiewicz ret = -EIDRM;
58600a2430fSAndrzej Pietrasiewicz break;
58700a2430fSAndrzej Pietrasiewicz }
58800a2430fSAndrzej Pietrasiewicz
58900a2430fSAndrzej Pietrasiewicz /* unlocks spinlock */
59000a2430fSAndrzej Pietrasiewicz ret = __ffs_ep0_queue_wait(ffs, data, len);
5918704fd73SGreg Kroah-Hartman if ((ret > 0) && (copy_to_user(buf, data, len)))
59200a2430fSAndrzej Pietrasiewicz ret = -EFAULT;
59300a2430fSAndrzej Pietrasiewicz goto done_mutex;
59400a2430fSAndrzej Pietrasiewicz
59500a2430fSAndrzej Pietrasiewicz default:
59600a2430fSAndrzej Pietrasiewicz ret = -EBADFD;
59700a2430fSAndrzej Pietrasiewicz break;
59800a2430fSAndrzej Pietrasiewicz }
59900a2430fSAndrzej Pietrasiewicz
60000a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&ffs->ev.waitq.lock);
60100a2430fSAndrzej Pietrasiewicz done_mutex:
60200a2430fSAndrzej Pietrasiewicz mutex_unlock(&ffs->mutex);
60300a2430fSAndrzej Pietrasiewicz kfree(data);
60400a2430fSAndrzej Pietrasiewicz return ret;
60500a2430fSAndrzej Pietrasiewicz }
60600a2430fSAndrzej Pietrasiewicz
ffs_ep0_open(struct inode * inode,struct file * file)60700a2430fSAndrzej Pietrasiewicz static int ffs_ep0_open(struct inode *inode, struct file *file)
60800a2430fSAndrzej Pietrasiewicz {
60900a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = inode->i_private;
61000a2430fSAndrzej Pietrasiewicz
6118704fd73SGreg Kroah-Hartman if (ffs->state == FFS_CLOSING)
61200a2430fSAndrzej Pietrasiewicz return -EBUSY;
61300a2430fSAndrzej Pietrasiewicz
61400a2430fSAndrzej Pietrasiewicz file->private_data = ffs;
61500a2430fSAndrzej Pietrasiewicz ffs_data_opened(ffs);
61600a2430fSAndrzej Pietrasiewicz
617c76ef96fSPavankumar Kondeti return stream_open(inode, file);
61800a2430fSAndrzej Pietrasiewicz }
61900a2430fSAndrzej Pietrasiewicz
ffs_ep0_release(struct inode * inode,struct file * file)62000a2430fSAndrzej Pietrasiewicz static int ffs_ep0_release(struct inode *inode, struct file *file)
62100a2430fSAndrzej Pietrasiewicz {
62200a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = file->private_data;
62300a2430fSAndrzej Pietrasiewicz
62400a2430fSAndrzej Pietrasiewicz ffs_data_closed(ffs);
62500a2430fSAndrzej Pietrasiewicz
62600a2430fSAndrzej Pietrasiewicz return 0;
62700a2430fSAndrzej Pietrasiewicz }
62800a2430fSAndrzej Pietrasiewicz
ffs_ep0_ioctl(struct file * file,unsigned code,unsigned long value)62900a2430fSAndrzej Pietrasiewicz static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
63000a2430fSAndrzej Pietrasiewicz {
63100a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = file->private_data;
63200a2430fSAndrzej Pietrasiewicz struct usb_gadget *gadget = ffs->gadget;
63300a2430fSAndrzej Pietrasiewicz long ret;
63400a2430fSAndrzej Pietrasiewicz
63500a2430fSAndrzej Pietrasiewicz if (code == FUNCTIONFS_INTERFACE_REVMAP) {
63600a2430fSAndrzej Pietrasiewicz struct ffs_function *func = ffs->func;
63700a2430fSAndrzej Pietrasiewicz ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
63800a2430fSAndrzej Pietrasiewicz } else if (gadget && gadget->ops->ioctl) {
63900a2430fSAndrzej Pietrasiewicz ret = gadget->ops->ioctl(gadget, code, value);
64000a2430fSAndrzej Pietrasiewicz } else {
64100a2430fSAndrzej Pietrasiewicz ret = -ENOTTY;
64200a2430fSAndrzej Pietrasiewicz }
64300a2430fSAndrzej Pietrasiewicz
64400a2430fSAndrzej Pietrasiewicz return ret;
64500a2430fSAndrzej Pietrasiewicz }
64600a2430fSAndrzej Pietrasiewicz
ffs_ep0_poll(struct file * file,poll_table * wait)647afc9a42bSAl Viro static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait)
64800a2430fSAndrzej Pietrasiewicz {
64900a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = file->private_data;
650a9a08845SLinus Torvalds __poll_t mask = EPOLLWRNORM;
65100a2430fSAndrzej Pietrasiewicz int ret;
65200a2430fSAndrzej Pietrasiewicz
65300a2430fSAndrzej Pietrasiewicz poll_wait(file, &ffs->ev.waitq, wait);
65400a2430fSAndrzej Pietrasiewicz
65500a2430fSAndrzej Pietrasiewicz ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
6568704fd73SGreg Kroah-Hartman if (ret < 0)
65700a2430fSAndrzej Pietrasiewicz return mask;
65800a2430fSAndrzej Pietrasiewicz
65900a2430fSAndrzej Pietrasiewicz switch (ffs->state) {
66000a2430fSAndrzej Pietrasiewicz case FFS_READ_DESCRIPTORS:
66100a2430fSAndrzej Pietrasiewicz case FFS_READ_STRINGS:
662a9a08845SLinus Torvalds mask |= EPOLLOUT;
66300a2430fSAndrzej Pietrasiewicz break;
66400a2430fSAndrzej Pietrasiewicz
66500a2430fSAndrzej Pietrasiewicz case FFS_ACTIVE:
66600a2430fSAndrzej Pietrasiewicz switch (ffs->setup_state) {
66700a2430fSAndrzej Pietrasiewicz case FFS_NO_SETUP:
66800a2430fSAndrzej Pietrasiewicz if (ffs->ev.count)
669a9a08845SLinus Torvalds mask |= EPOLLIN;
67000a2430fSAndrzej Pietrasiewicz break;
67100a2430fSAndrzej Pietrasiewicz
67200a2430fSAndrzej Pietrasiewicz case FFS_SETUP_PENDING:
67300a2430fSAndrzej Pietrasiewicz case FFS_SETUP_CANCELLED:
674a9a08845SLinus Torvalds mask |= (EPOLLIN | EPOLLOUT);
67500a2430fSAndrzej Pietrasiewicz break;
67600a2430fSAndrzej Pietrasiewicz }
67793c747edSGustavo A. R. Silva break;
67893c747edSGustavo A. R. Silva
67900a2430fSAndrzej Pietrasiewicz case FFS_CLOSING:
68000a2430fSAndrzej Pietrasiewicz break;
68118d6b32fSRobert Baldyga case FFS_DEACTIVATED:
68218d6b32fSRobert Baldyga break;
68300a2430fSAndrzej Pietrasiewicz }
68400a2430fSAndrzej Pietrasiewicz
68500a2430fSAndrzej Pietrasiewicz mutex_unlock(&ffs->mutex);
68600a2430fSAndrzej Pietrasiewicz
68700a2430fSAndrzej Pietrasiewicz return mask;
68800a2430fSAndrzej Pietrasiewicz }
68900a2430fSAndrzej Pietrasiewicz
69000a2430fSAndrzej Pietrasiewicz static const struct file_operations ffs_ep0_operations = {
69100a2430fSAndrzej Pietrasiewicz .llseek = no_llseek,
69200a2430fSAndrzej Pietrasiewicz
69300a2430fSAndrzej Pietrasiewicz .open = ffs_ep0_open,
69400a2430fSAndrzej Pietrasiewicz .write = ffs_ep0_write,
69500a2430fSAndrzej Pietrasiewicz .read = ffs_ep0_read,
69600a2430fSAndrzej Pietrasiewicz .release = ffs_ep0_release,
69700a2430fSAndrzej Pietrasiewicz .unlocked_ioctl = ffs_ep0_ioctl,
69800a2430fSAndrzej Pietrasiewicz .poll = ffs_ep0_poll,
69900a2430fSAndrzej Pietrasiewicz };
70000a2430fSAndrzej Pietrasiewicz
70100a2430fSAndrzej Pietrasiewicz
70200a2430fSAndrzej Pietrasiewicz /* "Normal" endpoints operations ********************************************/
70300a2430fSAndrzej Pietrasiewicz
ffs_epfile_io_complete(struct usb_ep * _ep,struct usb_request * req)70400a2430fSAndrzej Pietrasiewicz static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
70500a2430fSAndrzej Pietrasiewicz {
706fb1f16d7SLinyu Yuan struct ffs_io_data *io_data = req->context;
707fb1f16d7SLinyu Yuan
708fb1f16d7SLinyu Yuan if (req->status)
709fb1f16d7SLinyu Yuan io_data->status = req->status;
710fb1f16d7SLinyu Yuan else
711fb1f16d7SLinyu Yuan io_data->status = req->actual;
712fb1f16d7SLinyu Yuan
713fb1f16d7SLinyu Yuan complete(&io_data->done);
71400a2430fSAndrzej Pietrasiewicz }
71500a2430fSAndrzej Pietrasiewicz
ffs_copy_to_iter(void * data,int data_len,struct iov_iter * iter)716c662a31bSMichal Nazarewicz static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
717c662a31bSMichal Nazarewicz {
718c662a31bSMichal Nazarewicz ssize_t ret = copy_to_iter(data, data_len, iter);
7198704fd73SGreg Kroah-Hartman if (ret == data_len)
720c662a31bSMichal Nazarewicz return ret;
721c662a31bSMichal Nazarewicz
7228704fd73SGreg Kroah-Hartman if (iov_iter_count(iter))
723c662a31bSMichal Nazarewicz return -EFAULT;
724c662a31bSMichal Nazarewicz
725c662a31bSMichal Nazarewicz /*
726c662a31bSMichal Nazarewicz * Dear user space developer!
727c662a31bSMichal Nazarewicz *
728c662a31bSMichal Nazarewicz * TL;DR: To stop getting below error message in your kernel log, change
729c662a31bSMichal Nazarewicz * user space code using functionfs to align read buffers to a max
730c662a31bSMichal Nazarewicz * packet size.
731c662a31bSMichal Nazarewicz *
732c662a31bSMichal Nazarewicz * Some UDCs (e.g. dwc3) require request sizes to be a multiple of a max
733c662a31bSMichal Nazarewicz * packet size. When unaligned buffer is passed to functionfs, it
734c662a31bSMichal Nazarewicz * internally uses a larger, aligned buffer so that such UDCs are happy.
735c662a31bSMichal Nazarewicz *
736c662a31bSMichal Nazarewicz * Unfortunately, this means that host may send more data than was
737c662a31bSMichal Nazarewicz * requested in read(2) system call. f_fs doesn’t know what to do with
738c662a31bSMichal Nazarewicz * that excess data so it simply drops it.
739c662a31bSMichal Nazarewicz *
740c662a31bSMichal Nazarewicz * Was the buffer aligned in the first place, no such problem would
741c662a31bSMichal Nazarewicz * happen.
742c662a31bSMichal Nazarewicz *
7439353afbbSMichal Nazarewicz * Data may be dropped only in AIO reads. Synchronous reads are handled
7449353afbbSMichal Nazarewicz * by splitting a request into multiple parts. This splitting may still
7459353afbbSMichal Nazarewicz * be a problem though so it’s likely best to align the buffer
7469353afbbSMichal Nazarewicz * regardless of it being AIO or not..
7479353afbbSMichal Nazarewicz *
748c662a31bSMichal Nazarewicz * This only affects OUT endpoints, i.e. reading data with a read(2),
749c662a31bSMichal Nazarewicz * aio_read(2) etc. system calls. Writing data to an IN endpoint is not
750c662a31bSMichal Nazarewicz * affected.
751c662a31bSMichal Nazarewicz */
752c662a31bSMichal Nazarewicz pr_err("functionfs read size %d > requested size %zd, dropping excess data. "
753c662a31bSMichal Nazarewicz "Align read buffer size to max packet size to avoid the problem.\n",
754c662a31bSMichal Nazarewicz data_len, ret);
755c662a31bSMichal Nazarewicz
756c662a31bSMichal Nazarewicz return ret;
757c662a31bSMichal Nazarewicz }
758c662a31bSMichal Nazarewicz
759772a7a72SAndrzej Pietrasiewicz /*
760772a7a72SAndrzej Pietrasiewicz * allocate a virtually contiguous buffer and create a scatterlist describing it
761772a7a72SAndrzej Pietrasiewicz * @sg_table - pointer to a place to be filled with sg_table contents
762772a7a72SAndrzej Pietrasiewicz * @size - required buffer size
763772a7a72SAndrzej Pietrasiewicz */
ffs_build_sg_list(struct sg_table * sgt,size_t sz)764772a7a72SAndrzej Pietrasiewicz static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz)
765772a7a72SAndrzej Pietrasiewicz {
766772a7a72SAndrzej Pietrasiewicz struct page **pages;
767772a7a72SAndrzej Pietrasiewicz void *vaddr, *ptr;
768772a7a72SAndrzej Pietrasiewicz unsigned int n_pages;
769772a7a72SAndrzej Pietrasiewicz int i;
770772a7a72SAndrzej Pietrasiewicz
771772a7a72SAndrzej Pietrasiewicz vaddr = vmalloc(sz);
772772a7a72SAndrzej Pietrasiewicz if (!vaddr)
773772a7a72SAndrzej Pietrasiewicz return NULL;
774772a7a72SAndrzej Pietrasiewicz
775772a7a72SAndrzej Pietrasiewicz n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
776772a7a72SAndrzej Pietrasiewicz pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
777772a7a72SAndrzej Pietrasiewicz if (!pages) {
778772a7a72SAndrzej Pietrasiewicz vfree(vaddr);
779772a7a72SAndrzej Pietrasiewicz
780772a7a72SAndrzej Pietrasiewicz return NULL;
781772a7a72SAndrzej Pietrasiewicz }
782772a7a72SAndrzej Pietrasiewicz for (i = 0, ptr = vaddr; i < n_pages; ++i, ptr += PAGE_SIZE)
783772a7a72SAndrzej Pietrasiewicz pages[i] = vmalloc_to_page(ptr);
784772a7a72SAndrzej Pietrasiewicz
785772a7a72SAndrzej Pietrasiewicz if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) {
786772a7a72SAndrzej Pietrasiewicz kvfree(pages);
787772a7a72SAndrzej Pietrasiewicz vfree(vaddr);
788772a7a72SAndrzej Pietrasiewicz
789772a7a72SAndrzej Pietrasiewicz return NULL;
790772a7a72SAndrzej Pietrasiewicz }
791772a7a72SAndrzej Pietrasiewicz kvfree(pages);
792772a7a72SAndrzej Pietrasiewicz
793772a7a72SAndrzej Pietrasiewicz return vaddr;
794772a7a72SAndrzej Pietrasiewicz }
795772a7a72SAndrzej Pietrasiewicz
ffs_alloc_buffer(struct ffs_io_data * io_data,size_t data_len)796772a7a72SAndrzej Pietrasiewicz static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data,
797772a7a72SAndrzej Pietrasiewicz size_t data_len)
798772a7a72SAndrzej Pietrasiewicz {
799772a7a72SAndrzej Pietrasiewicz if (io_data->use_sg)
800772a7a72SAndrzej Pietrasiewicz return ffs_build_sg_list(&io_data->sgt, data_len);
801772a7a72SAndrzej Pietrasiewicz
802772a7a72SAndrzej Pietrasiewicz return kmalloc(data_len, GFP_KERNEL);
803772a7a72SAndrzej Pietrasiewicz }
804772a7a72SAndrzej Pietrasiewicz
ffs_free_buffer(struct ffs_io_data * io_data)805772a7a72SAndrzej Pietrasiewicz static inline void ffs_free_buffer(struct ffs_io_data *io_data)
806772a7a72SAndrzej Pietrasiewicz {
807772a7a72SAndrzej Pietrasiewicz if (!io_data->buf)
808772a7a72SAndrzej Pietrasiewicz return;
809772a7a72SAndrzej Pietrasiewicz
810772a7a72SAndrzej Pietrasiewicz if (io_data->use_sg) {
811772a7a72SAndrzej Pietrasiewicz sg_free_table(&io_data->sgt);
812772a7a72SAndrzej Pietrasiewicz vfree(io_data->buf);
813772a7a72SAndrzej Pietrasiewicz } else {
814772a7a72SAndrzej Pietrasiewicz kfree(io_data->buf);
815772a7a72SAndrzej Pietrasiewicz }
816772a7a72SAndrzej Pietrasiewicz }
817772a7a72SAndrzej Pietrasiewicz
ffs_user_copy_worker(struct work_struct * work)81800a2430fSAndrzej Pietrasiewicz static void ffs_user_copy_worker(struct work_struct *work)
81900a2430fSAndrzej Pietrasiewicz {
82000a2430fSAndrzej Pietrasiewicz struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
82100a2430fSAndrzej Pietrasiewicz work);
822b566d388SJohn Keeping int ret = io_data->status;
82338740a5bSLars-Peter Clausen bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
82473c05ad4SWesley Cheng unsigned long flags;
82500a2430fSAndrzej Pietrasiewicz
82600a2430fSAndrzej Pietrasiewicz if (io_data->read && ret > 0) {
827f5678e7fSChristoph Hellwig kthread_use_mm(io_data->mm);
828c662a31bSMichal Nazarewicz ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
829f5678e7fSChristoph Hellwig kthread_unuse_mm(io_data->mm);
83000a2430fSAndrzej Pietrasiewicz }
83100a2430fSAndrzej Pietrasiewicz
8326b19b766SJens Axboe io_data->kiocb->ki_complete(io_data->kiocb, ret);
83300a2430fSAndrzej Pietrasiewicz
83438740a5bSLars-Peter Clausen if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)
8355e33f6fdSRobert Baldyga eventfd_signal(io_data->ffs->ffs_eventfd, 1);
8365e33f6fdSRobert Baldyga
83773c05ad4SWesley Cheng spin_lock_irqsave(&io_data->ffs->eps_lock, flags);
83873c05ad4SWesley Cheng usb_ep_free_request(io_data->ep, io_data->req);
83973c05ad4SWesley Cheng io_data->req = NULL;
84073c05ad4SWesley Cheng spin_unlock_irqrestore(&io_data->ffs->eps_lock, flags);
84173c05ad4SWesley Cheng
84200a2430fSAndrzej Pietrasiewicz if (io_data->read)
843c993c39bSAl Viro kfree(io_data->to_free);
844772a7a72SAndrzej Pietrasiewicz ffs_free_buffer(io_data);
84500a2430fSAndrzej Pietrasiewicz kfree(io_data);
84600a2430fSAndrzej Pietrasiewicz }
84700a2430fSAndrzej Pietrasiewicz
ffs_epfile_async_io_complete(struct usb_ep * _ep,struct usb_request * req)84800a2430fSAndrzej Pietrasiewicz static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
84900a2430fSAndrzej Pietrasiewicz struct usb_request *req)
85000a2430fSAndrzej Pietrasiewicz {
85100a2430fSAndrzej Pietrasiewicz struct ffs_io_data *io_data = req->context;
852addfc582SJohn Keeping struct ffs_data *ffs = io_data->ffs;
85300a2430fSAndrzej Pietrasiewicz
854b566d388SJohn Keeping io_data->status = req->status ? req->status : req->actual;
855b566d388SJohn Keeping
85600a2430fSAndrzej Pietrasiewicz INIT_WORK(&io_data->work, ffs_user_copy_worker);
857addfc582SJohn Keeping queue_work(ffs->io_completion_wq, &io_data->work);
85800a2430fSAndrzej Pietrasiewicz }
85900a2430fSAndrzej Pietrasiewicz
__ffs_epfile_read_buffer_free(struct ffs_epfile * epfile)860a9e6f83cSMichal Nazarewicz static void __ffs_epfile_read_buffer_free(struct ffs_epfile *epfile)
861a9e6f83cSMichal Nazarewicz {
862a9e6f83cSMichal Nazarewicz /*
863a9e6f83cSMichal Nazarewicz * See comment in struct ffs_epfile for full read_buffer pointer
864a9e6f83cSMichal Nazarewicz * synchronisation story.
865a9e6f83cSMichal Nazarewicz */
866a9e6f83cSMichal Nazarewicz struct ffs_buffer *buf = xchg(&epfile->read_buffer, READ_BUFFER_DROP);
867a9e6f83cSMichal Nazarewicz if (buf && buf != READ_BUFFER_DROP)
868a9e6f83cSMichal Nazarewicz kfree(buf);
869a9e6f83cSMichal Nazarewicz }
870a9e6f83cSMichal Nazarewicz
8719353afbbSMichal Nazarewicz /* Assumes epfile->mutex is held. */
__ffs_epfile_read_buffered(struct ffs_epfile * epfile,struct iov_iter * iter)8729353afbbSMichal Nazarewicz static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
8739353afbbSMichal Nazarewicz struct iov_iter *iter)
8749353afbbSMichal Nazarewicz {
875a9e6f83cSMichal Nazarewicz /*
876a9e6f83cSMichal Nazarewicz * Null out epfile->read_buffer so ffs_func_eps_disable does not free
877a9e6f83cSMichal Nazarewicz * the buffer while we are using it. See comment in struct ffs_epfile
878a9e6f83cSMichal Nazarewicz * for full read_buffer pointer synchronisation story.
879a9e6f83cSMichal Nazarewicz */
880a9e6f83cSMichal Nazarewicz struct ffs_buffer *buf = xchg(&epfile->read_buffer, NULL);
8819353afbbSMichal Nazarewicz ssize_t ret;
882a9e6f83cSMichal Nazarewicz if (!buf || buf == READ_BUFFER_DROP)
8839353afbbSMichal Nazarewicz return 0;
8849353afbbSMichal Nazarewicz
8859353afbbSMichal Nazarewicz ret = copy_to_iter(buf->data, buf->length, iter);
8869353afbbSMichal Nazarewicz if (buf->length == ret) {
8879353afbbSMichal Nazarewicz kfree(buf);
888a9e6f83cSMichal Nazarewicz return ret;
889a9e6f83cSMichal Nazarewicz }
890a9e6f83cSMichal Nazarewicz
8918704fd73SGreg Kroah-Hartman if (iov_iter_count(iter)) {
8929353afbbSMichal Nazarewicz ret = -EFAULT;
8939353afbbSMichal Nazarewicz } else {
8949353afbbSMichal Nazarewicz buf->length -= ret;
8959353afbbSMichal Nazarewicz buf->data += ret;
8969353afbbSMichal Nazarewicz }
897a9e6f83cSMichal Nazarewicz
898a9e6f83cSMichal Nazarewicz if (cmpxchg(&epfile->read_buffer, NULL, buf))
899a9e6f83cSMichal Nazarewicz kfree(buf);
900a9e6f83cSMichal Nazarewicz
9019353afbbSMichal Nazarewicz return ret;
9029353afbbSMichal Nazarewicz }
9039353afbbSMichal Nazarewicz
9049353afbbSMichal Nazarewicz /* Assumes epfile->mutex is held. */
__ffs_epfile_read_data(struct ffs_epfile * epfile,void * data,int data_len,struct iov_iter * iter)9059353afbbSMichal Nazarewicz static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
9069353afbbSMichal Nazarewicz void *data, int data_len,
9079353afbbSMichal Nazarewicz struct iov_iter *iter)
9089353afbbSMichal Nazarewicz {
9099353afbbSMichal Nazarewicz struct ffs_buffer *buf;
9109353afbbSMichal Nazarewicz
9119353afbbSMichal Nazarewicz ssize_t ret = copy_to_iter(data, data_len, iter);
9128704fd73SGreg Kroah-Hartman if (data_len == ret)
9139353afbbSMichal Nazarewicz return ret;
9149353afbbSMichal Nazarewicz
9158704fd73SGreg Kroah-Hartman if (iov_iter_count(iter))
9169353afbbSMichal Nazarewicz return -EFAULT;
9179353afbbSMichal Nazarewicz
9189353afbbSMichal Nazarewicz /* See ffs_copy_to_iter for more context. */
9199353afbbSMichal Nazarewicz pr_warn("functionfs read size %d > requested size %zd, splitting request into multiple reads.",
9209353afbbSMichal Nazarewicz data_len, ret);
9219353afbbSMichal Nazarewicz
9229353afbbSMichal Nazarewicz data_len -= ret;
9234213e92eSGustavo A. R. Silva buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL);
92444963d64SDan Carpenter if (!buf)
92544963d64SDan Carpenter return -ENOMEM;
9269353afbbSMichal Nazarewicz buf->length = data_len;
9279353afbbSMichal Nazarewicz buf->data = buf->storage;
9284213e92eSGustavo A. R. Silva memcpy(buf->storage, data + ret, flex_array_size(buf, storage, data_len));
929a9e6f83cSMichal Nazarewicz
930a9e6f83cSMichal Nazarewicz /*
931a9e6f83cSMichal Nazarewicz * At this point read_buffer is NULL or READ_BUFFER_DROP (if
932a9e6f83cSMichal Nazarewicz * ffs_func_eps_disable has been called in the meanwhile). See comment
933a9e6f83cSMichal Nazarewicz * in struct ffs_epfile for full read_buffer pointer synchronisation
934a9e6f83cSMichal Nazarewicz * story.
935a9e6f83cSMichal Nazarewicz */
9368704fd73SGreg Kroah-Hartman if (cmpxchg(&epfile->read_buffer, NULL, buf))
937a9e6f83cSMichal Nazarewicz kfree(buf);
9389353afbbSMichal Nazarewicz
9399353afbbSMichal Nazarewicz return ret;
9409353afbbSMichal Nazarewicz }
9419353afbbSMichal Nazarewicz
ffs_epfile_io(struct file * file,struct ffs_io_data * io_data)94200a2430fSAndrzej Pietrasiewicz static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
94300a2430fSAndrzej Pietrasiewicz {
94400a2430fSAndrzej Pietrasiewicz struct ffs_epfile *epfile = file->private_data;
945ae76e134SMichal Nazarewicz struct usb_request *req;
94600a2430fSAndrzej Pietrasiewicz struct ffs_ep *ep;
94700a2430fSAndrzej Pietrasiewicz char *data = NULL;
948c0d31b3cSDavid Cohen ssize_t ret, data_len = -EINVAL;
94900a2430fSAndrzej Pietrasiewicz int halt;
95000a2430fSAndrzej Pietrasiewicz
95100a2430fSAndrzej Pietrasiewicz /* Are we still active? */
952b3591f67SMichal Nazarewicz if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
953b3591f67SMichal Nazarewicz return -ENODEV;
95400a2430fSAndrzej Pietrasiewicz
95500a2430fSAndrzej Pietrasiewicz /* Wait for endpoint to be enabled */
95600a2430fSAndrzej Pietrasiewicz ep = epfile->ep;
95700a2430fSAndrzej Pietrasiewicz if (!ep) {
958b3591f67SMichal Nazarewicz if (file->f_flags & O_NONBLOCK)
959b3591f67SMichal Nazarewicz return -EAGAIN;
96000a2430fSAndrzej Pietrasiewicz
961e16828cfSJerry Zhang ret = wait_event_interruptible(
962e16828cfSJerry Zhang epfile->ffs->wait, (ep = epfile->ep));
963b3591f67SMichal Nazarewicz if (ret)
964b3591f67SMichal Nazarewicz return -EINTR;
96500a2430fSAndrzej Pietrasiewicz }
96600a2430fSAndrzej Pietrasiewicz
96700a2430fSAndrzej Pietrasiewicz /* Do we halt? */
96800a2430fSAndrzej Pietrasiewicz halt = (!io_data->read == !epfile->in);
969b3591f67SMichal Nazarewicz if (halt && epfile->isoc)
970b3591f67SMichal Nazarewicz return -EINVAL;
97100a2430fSAndrzej Pietrasiewicz
9729353afbbSMichal Nazarewicz /* We will be using request and read_buffer */
9739353afbbSMichal Nazarewicz ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
9748704fd73SGreg Kroah-Hartman if (ret)
9759353afbbSMichal Nazarewicz goto error;
9769353afbbSMichal Nazarewicz
97700a2430fSAndrzej Pietrasiewicz /* Allocate & copy */
97800a2430fSAndrzej Pietrasiewicz if (!halt) {
9799353afbbSMichal Nazarewicz struct usb_gadget *gadget;
9809353afbbSMichal Nazarewicz
9819353afbbSMichal Nazarewicz /*
9829353afbbSMichal Nazarewicz * Do we have buffered data from previous partial read? Check
9839353afbbSMichal Nazarewicz * that for synchronous case only because we do not have
9849353afbbSMichal Nazarewicz * facility to ‘wake up’ a pending asynchronous read and push
9859353afbbSMichal Nazarewicz * buffered data to it which we would need to make things behave
9869353afbbSMichal Nazarewicz * consistently.
9879353afbbSMichal Nazarewicz */
9889353afbbSMichal Nazarewicz if (!io_data->aio && io_data->read) {
9899353afbbSMichal Nazarewicz ret = __ffs_epfile_read_buffered(epfile, &io_data->data);
9909353afbbSMichal Nazarewicz if (ret)
9919353afbbSMichal Nazarewicz goto error_mutex;
9929353afbbSMichal Nazarewicz }
9939353afbbSMichal Nazarewicz
99400a2430fSAndrzej Pietrasiewicz /*
99500a2430fSAndrzej Pietrasiewicz * if we _do_ wait above, the epfile->ffs->gadget might be NULL
996ae76e134SMichal Nazarewicz * before the waiting completes, so do not assign to 'gadget'
997ae76e134SMichal Nazarewicz * earlier
99800a2430fSAndrzej Pietrasiewicz */
9999353afbbSMichal Nazarewicz gadget = epfile->ffs->gadget;
100000a2430fSAndrzej Pietrasiewicz
100100a2430fSAndrzej Pietrasiewicz spin_lock_irq(&epfile->ffs->eps_lock);
100200a2430fSAndrzej Pietrasiewicz /* In the meantime, endpoint got disabled or changed. */
100300a2430fSAndrzej Pietrasiewicz if (epfile->ep != ep) {
10049353afbbSMichal Nazarewicz ret = -ESHUTDOWN;
10059353afbbSMichal Nazarewicz goto error_lock;
100600a2430fSAndrzej Pietrasiewicz }
1007c993c39bSAl Viro data_len = iov_iter_count(&io_data->data);
100800a2430fSAndrzej Pietrasiewicz /*
100900a2430fSAndrzej Pietrasiewicz * Controller may require buffer size to be aligned to
101000a2430fSAndrzej Pietrasiewicz * maxpacketsize of an out endpoint.
101100a2430fSAndrzej Pietrasiewicz */
1012c993c39bSAl Viro if (io_data->read)
1013c993c39bSAl Viro data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
10144833a94eSFei Yang
10154833a94eSFei Yang io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE;
101600a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&epfile->ffs->eps_lock);
101700a2430fSAndrzej Pietrasiewicz
1018772a7a72SAndrzej Pietrasiewicz data = ffs_alloc_buffer(io_data, data_len);
10198704fd73SGreg Kroah-Hartman if (!data) {
10209353afbbSMichal Nazarewicz ret = -ENOMEM;
10219353afbbSMichal Nazarewicz goto error_mutex;
10229353afbbSMichal Nazarewicz }
10239353afbbSMichal Nazarewicz if (!io_data->read &&
1024cbbd26b8SAl Viro !copy_from_iter_full(data, data_len, &io_data->data)) {
102500a2430fSAndrzej Pietrasiewicz ret = -EFAULT;
10269353afbbSMichal Nazarewicz goto error_mutex;
102700a2430fSAndrzej Pietrasiewicz }
102800a2430fSAndrzej Pietrasiewicz }
102900a2430fSAndrzej Pietrasiewicz
103000a2430fSAndrzej Pietrasiewicz spin_lock_irq(&epfile->ffs->eps_lock);
103100a2430fSAndrzej Pietrasiewicz
103200a2430fSAndrzej Pietrasiewicz if (epfile->ep != ep) {
103300a2430fSAndrzej Pietrasiewicz /* In the meantime, endpoint got disabled or changed. */
103400a2430fSAndrzej Pietrasiewicz ret = -ESHUTDOWN;
103500a2430fSAndrzej Pietrasiewicz } else if (halt) {
1036cdff9f8eSJerry Zhang ret = usb_ep_set_halt(ep->ep);
1037cdff9f8eSJerry Zhang if (!ret)
103800a2430fSAndrzej Pietrasiewicz ret = -EBADMSG;
10398704fd73SGreg Kroah-Hartman } else if (data_len == -EINVAL) {
1040c0d31b3cSDavid Cohen /*
1041c0d31b3cSDavid Cohen * Sanity Check: even though data_len can't be used
1042c0d31b3cSDavid Cohen * uninitialized at the time I write this comment, some
1043c0d31b3cSDavid Cohen * compilers complain about this situation.
1044c0d31b3cSDavid Cohen * In order to keep the code clean from warnings, data_len is
1045c0d31b3cSDavid Cohen * being initialized to -EINVAL during its declaration, which
1046c0d31b3cSDavid Cohen * means we can't rely on compiler anymore to warn no future
1047c0d31b3cSDavid Cohen * changes won't result in data_len being used uninitialized.
1048c0d31b3cSDavid Cohen * For such reason, we're adding this redundant sanity check
1049c0d31b3cSDavid Cohen * here.
1050c0d31b3cSDavid Cohen */
1051c0d31b3cSDavid Cohen WARN(1, "%s: data_len == -EINVAL\n", __func__);
1052c0d31b3cSDavid Cohen ret = -EINVAL;
1053ae76e134SMichal Nazarewicz } else if (!io_data->aio) {
1054ef150884SDu, Changbin bool interrupted = false;
1055ae76e134SMichal Nazarewicz
1056ae76e134SMichal Nazarewicz req = ep->req;
1057772a7a72SAndrzej Pietrasiewicz if (io_data->use_sg) {
1058772a7a72SAndrzej Pietrasiewicz req->buf = NULL;
1059772a7a72SAndrzej Pietrasiewicz req->sg = io_data->sgt.sgl;
1060772a7a72SAndrzej Pietrasiewicz req->num_sgs = io_data->sgt.nents;
1061772a7a72SAndrzej Pietrasiewicz } else {
1062ae76e134SMichal Nazarewicz req->buf = data;
1063d2450c69SPeter Chen req->num_sgs = 0;
1064772a7a72SAndrzej Pietrasiewicz }
1065ae76e134SMichal Nazarewicz req->length = data_len;
1066ae76e134SMichal Nazarewicz
1067772a7a72SAndrzej Pietrasiewicz io_data->buf = data;
1068772a7a72SAndrzej Pietrasiewicz
1069fb1f16d7SLinyu Yuan init_completion(&io_data->done);
1070fb1f16d7SLinyu Yuan req->context = io_data;
1071ae76e134SMichal Nazarewicz req->complete = ffs_epfile_io_complete;
1072ae76e134SMichal Nazarewicz
1073ae76e134SMichal Nazarewicz ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
10748704fd73SGreg Kroah-Hartman if (ret < 0)
1075c0d31b3cSDavid Cohen goto error_lock;
1076ae76e134SMichal Nazarewicz
1077ae76e134SMichal Nazarewicz spin_unlock_irq(&epfile->ffs->eps_lock);
1078ae76e134SMichal Nazarewicz
1079fb1f16d7SLinyu Yuan if (wait_for_completion_interruptible(&io_data->done)) {
10800698f020SLinyu Yuan spin_lock_irq(&epfile->ffs->eps_lock);
10810698f020SLinyu Yuan if (epfile->ep != ep) {
10820698f020SLinyu Yuan ret = -ESHUTDOWN;
10830698f020SLinyu Yuan goto error_lock;
10840698f020SLinyu Yuan }
1085ef150884SDu, Changbin /*
1086ef150884SDu, Changbin * To avoid race condition with ffs_epfile_io_complete,
1087ef150884SDu, Changbin * dequeue the request first then check
1088ef150884SDu, Changbin * status. usb_ep_dequeue API should guarantee no race
1089ef150884SDu, Changbin * condition with req->complete callback.
1090ef150884SDu, Changbin */
1091ae76e134SMichal Nazarewicz usb_ep_dequeue(ep->ep, req);
10920698f020SLinyu Yuan spin_unlock_irq(&epfile->ffs->eps_lock);
1093fb1f16d7SLinyu Yuan wait_for_completion(&io_data->done);
1094fb1f16d7SLinyu Yuan interrupted = io_data->status < 0;
1095c0d31b3cSDavid Cohen }
1096c0d31b3cSDavid Cohen
1097c662a31bSMichal Nazarewicz if (interrupted)
1098c662a31bSMichal Nazarewicz ret = -EINTR;
1099fb1f16d7SLinyu Yuan else if (io_data->read && io_data->status > 0)
1100fb1f16d7SLinyu Yuan ret = __ffs_epfile_read_data(epfile, data, io_data->status,
1101c662a31bSMichal Nazarewicz &io_data->data);
1102c662a31bSMichal Nazarewicz else
1103fb1f16d7SLinyu Yuan ret = io_data->status;
1104ae76e134SMichal Nazarewicz goto error_mutex;
110530bf90ccSVincent Pelletier } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) {
11063163c79eSMichal Nazarewicz ret = -ENOMEM;
1107ae76e134SMichal Nazarewicz } else {
1108772a7a72SAndrzej Pietrasiewicz if (io_data->use_sg) {
1109772a7a72SAndrzej Pietrasiewicz req->buf = NULL;
1110772a7a72SAndrzej Pietrasiewicz req->sg = io_data->sgt.sgl;
1111772a7a72SAndrzej Pietrasiewicz req->num_sgs = io_data->sgt.nents;
1112772a7a72SAndrzej Pietrasiewicz } else {
111300a2430fSAndrzej Pietrasiewicz req->buf = data;
1114d2450c69SPeter Chen req->num_sgs = 0;
1115772a7a72SAndrzej Pietrasiewicz }
1116c0d31b3cSDavid Cohen req->length = data_len;
111700a2430fSAndrzej Pietrasiewicz
111800a2430fSAndrzej Pietrasiewicz io_data->buf = data;
111900a2430fSAndrzej Pietrasiewicz io_data->ep = ep->ep;
112000a2430fSAndrzej Pietrasiewicz io_data->req = req;
11215e33f6fdSRobert Baldyga io_data->ffs = epfile->ffs;
112200a2430fSAndrzej Pietrasiewicz
112300a2430fSAndrzej Pietrasiewicz req->context = io_data;
112400a2430fSAndrzej Pietrasiewicz req->complete = ffs_epfile_async_io_complete;
112500a2430fSAndrzej Pietrasiewicz
112600a2430fSAndrzej Pietrasiewicz ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
11278704fd73SGreg Kroah-Hartman if (ret) {
1128f63ec55fSSriharsha Allenki io_data->req = NULL;
112900a2430fSAndrzej Pietrasiewicz usb_ep_free_request(ep->ep, req);
113000a2430fSAndrzej Pietrasiewicz goto error_lock;
113100a2430fSAndrzej Pietrasiewicz }
1132ae76e134SMichal Nazarewicz
113300a2430fSAndrzej Pietrasiewicz ret = -EIOCBQUEUED;
113400a2430fSAndrzej Pietrasiewicz /*
1135ae76e134SMichal Nazarewicz * Do not kfree the buffer in this function. It will be freed
1136ae76e134SMichal Nazarewicz * by ffs_user_copy_worker.
113700a2430fSAndrzej Pietrasiewicz */
1138ae76e134SMichal Nazarewicz data = NULL;
113900a2430fSAndrzej Pietrasiewicz }
114000a2430fSAndrzej Pietrasiewicz
114100a2430fSAndrzej Pietrasiewicz error_lock:
114200a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&epfile->ffs->eps_lock);
1143ae76e134SMichal Nazarewicz error_mutex:
114400a2430fSAndrzej Pietrasiewicz mutex_unlock(&epfile->mutex);
114500a2430fSAndrzej Pietrasiewicz error:
114673103c7fSFei Yang if (ret != -EIOCBQUEUED) /* don't free if there is iocb queued */
1147772a7a72SAndrzej Pietrasiewicz ffs_free_buffer(io_data);
114800a2430fSAndrzej Pietrasiewicz return ret;
114900a2430fSAndrzej Pietrasiewicz }
115000a2430fSAndrzej Pietrasiewicz
115100a2430fSAndrzej Pietrasiewicz static int
ffs_epfile_open(struct inode * inode,struct file * file)115200a2430fSAndrzej Pietrasiewicz ffs_epfile_open(struct inode *inode, struct file *file)
115300a2430fSAndrzej Pietrasiewicz {
115400a2430fSAndrzej Pietrasiewicz struct ffs_epfile *epfile = inode->i_private;
115500a2430fSAndrzej Pietrasiewicz
115600a2430fSAndrzej Pietrasiewicz if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
115700a2430fSAndrzej Pietrasiewicz return -ENODEV;
115800a2430fSAndrzej Pietrasiewicz
115900a2430fSAndrzej Pietrasiewicz file->private_data = epfile;
116000a2430fSAndrzej Pietrasiewicz ffs_data_opened(epfile->ffs);
116100a2430fSAndrzej Pietrasiewicz
1162c76ef96fSPavankumar Kondeti return stream_open(inode, file);
116300a2430fSAndrzej Pietrasiewicz }
116400a2430fSAndrzej Pietrasiewicz
ffs_aio_cancel(struct kiocb * kiocb)116500a2430fSAndrzej Pietrasiewicz static int ffs_aio_cancel(struct kiocb *kiocb)
116600a2430fSAndrzej Pietrasiewicz {
116700a2430fSAndrzej Pietrasiewicz struct ffs_io_data *io_data = kiocb->private;
1168a9c85903SShen Jing struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
116943d56572SLars-Peter Clausen unsigned long flags;
117000a2430fSAndrzej Pietrasiewicz int value;
117100a2430fSAndrzej Pietrasiewicz
117243d56572SLars-Peter Clausen spin_lock_irqsave(&epfile->ffs->eps_lock, flags);
1173a9c85903SShen Jing
11748704fd73SGreg Kroah-Hartman if (io_data && io_data->ep && io_data->req)
1175a9c85903SShen Jing value = usb_ep_dequeue(io_data->ep, io_data->req);
1176a9c85903SShen Jing else
117700a2430fSAndrzej Pietrasiewicz value = -EINVAL;
1178a9c85903SShen Jing
117943d56572SLars-Peter Clausen spin_unlock_irqrestore(&epfile->ffs->eps_lock, flags);
118000a2430fSAndrzej Pietrasiewicz
118100a2430fSAndrzej Pietrasiewicz return value;
118200a2430fSAndrzej Pietrasiewicz }
118300a2430fSAndrzej Pietrasiewicz
ffs_epfile_write_iter(struct kiocb * kiocb,struct iov_iter * from)118470e60d91SAl Viro static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
118500a2430fSAndrzej Pietrasiewicz {
118670e60d91SAl Viro struct ffs_io_data io_data, *p = &io_data;
1187de2080d4SAl Viro ssize_t res;
118800a2430fSAndrzej Pietrasiewicz
118970e60d91SAl Viro if (!is_sync_kiocb(kiocb)) {
119050859551SAndrzej Pietrasiewicz p = kzalloc(sizeof(io_data), GFP_KERNEL);
11918704fd73SGreg Kroah-Hartman if (!p)
119200a2430fSAndrzej Pietrasiewicz return -ENOMEM;
119370e60d91SAl Viro p->aio = true;
119470e60d91SAl Viro } else {
119550859551SAndrzej Pietrasiewicz memset(p, 0, sizeof(*p));
119670e60d91SAl Viro p->aio = false;
119770e60d91SAl Viro }
119800a2430fSAndrzej Pietrasiewicz
119970e60d91SAl Viro p->read = false;
120070e60d91SAl Viro p->kiocb = kiocb;
120170e60d91SAl Viro p->data = *from;
120270e60d91SAl Viro p->mm = current->mm;
120300a2430fSAndrzej Pietrasiewicz
120470e60d91SAl Viro kiocb->private = p;
120500a2430fSAndrzej Pietrasiewicz
12064088acf1SRui Miguel Silva if (p->aio)
120700a2430fSAndrzej Pietrasiewicz kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
120800a2430fSAndrzej Pietrasiewicz
120970e60d91SAl Viro res = ffs_epfile_io(kiocb->ki_filp, p);
121070e60d91SAl Viro if (res == -EIOCBQUEUED)
121170e60d91SAl Viro return res;
121270e60d91SAl Viro if (p->aio)
121370e60d91SAl Viro kfree(p);
121470e60d91SAl Viro else
121570e60d91SAl Viro *from = p->data;
1216de2080d4SAl Viro return res;
121700a2430fSAndrzej Pietrasiewicz }
121800a2430fSAndrzej Pietrasiewicz
ffs_epfile_read_iter(struct kiocb * kiocb,struct iov_iter * to)121970e60d91SAl Viro static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
122000a2430fSAndrzej Pietrasiewicz {
122170e60d91SAl Viro struct ffs_io_data io_data, *p = &io_data;
1222de2080d4SAl Viro ssize_t res;
122300a2430fSAndrzej Pietrasiewicz
122470e60d91SAl Viro if (!is_sync_kiocb(kiocb)) {
122550859551SAndrzej Pietrasiewicz p = kzalloc(sizeof(io_data), GFP_KERNEL);
12268704fd73SGreg Kroah-Hartman if (!p)
122700a2430fSAndrzej Pietrasiewicz return -ENOMEM;
122870e60d91SAl Viro p->aio = true;
122970e60d91SAl Viro } else {
123050859551SAndrzej Pietrasiewicz memset(p, 0, sizeof(*p));
123170e60d91SAl Viro p->aio = false;
123200a2430fSAndrzej Pietrasiewicz }
123300a2430fSAndrzej Pietrasiewicz
123470e60d91SAl Viro p->read = true;
123570e60d91SAl Viro p->kiocb = kiocb;
123670e60d91SAl Viro if (p->aio) {
123770e60d91SAl Viro p->to_free = dup_iter(&p->data, to, GFP_KERNEL);
1238d356b3cdSSandeep Dhavale if (!iter_is_ubuf(&p->data) && !p->to_free) {
123970e60d91SAl Viro kfree(p);
124070e60d91SAl Viro return -ENOMEM;
124170e60d91SAl Viro }
124270e60d91SAl Viro } else {
124370e60d91SAl Viro p->data = *to;
124470e60d91SAl Viro p->to_free = NULL;
124570e60d91SAl Viro }
124670e60d91SAl Viro p->mm = current->mm;
124700a2430fSAndrzej Pietrasiewicz
124870e60d91SAl Viro kiocb->private = p;
124900a2430fSAndrzej Pietrasiewicz
12504088acf1SRui Miguel Silva if (p->aio)
125100a2430fSAndrzej Pietrasiewicz kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
125200a2430fSAndrzej Pietrasiewicz
125370e60d91SAl Viro res = ffs_epfile_io(kiocb->ki_filp, p);
125470e60d91SAl Viro if (res == -EIOCBQUEUED)
125570e60d91SAl Viro return res;
125670e60d91SAl Viro
125770e60d91SAl Viro if (p->aio) {
125870e60d91SAl Viro kfree(p->to_free);
125970e60d91SAl Viro kfree(p);
126070e60d91SAl Viro } else {
126170e60d91SAl Viro *to = p->data;
1262de2080d4SAl Viro }
1263de2080d4SAl Viro return res;
126400a2430fSAndrzej Pietrasiewicz }
126500a2430fSAndrzej Pietrasiewicz
126600a2430fSAndrzej Pietrasiewicz static int
ffs_epfile_release(struct inode * inode,struct file * file)126700a2430fSAndrzej Pietrasiewicz ffs_epfile_release(struct inode *inode, struct file *file)
126800a2430fSAndrzej Pietrasiewicz {
126900a2430fSAndrzej Pietrasiewicz struct ffs_epfile *epfile = inode->i_private;
127000a2430fSAndrzej Pietrasiewicz
1271a9e6f83cSMichal Nazarewicz __ffs_epfile_read_buffer_free(epfile);
127200a2430fSAndrzej Pietrasiewicz ffs_data_closed(epfile->ffs);
127300a2430fSAndrzej Pietrasiewicz
127400a2430fSAndrzej Pietrasiewicz return 0;
127500a2430fSAndrzej Pietrasiewicz }
127600a2430fSAndrzej Pietrasiewicz
ffs_epfile_ioctl(struct file * file,unsigned code,unsigned long value)127700a2430fSAndrzej Pietrasiewicz static long ffs_epfile_ioctl(struct file *file, unsigned code,
127800a2430fSAndrzej Pietrasiewicz unsigned long value)
127900a2430fSAndrzej Pietrasiewicz {
128000a2430fSAndrzej Pietrasiewicz struct ffs_epfile *epfile = file->private_data;
1281222155deSJerry Zhang struct ffs_ep *ep;
128200a2430fSAndrzej Pietrasiewicz int ret;
128300a2430fSAndrzej Pietrasiewicz
128400a2430fSAndrzej Pietrasiewicz if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
128500a2430fSAndrzej Pietrasiewicz return -ENODEV;
128600a2430fSAndrzej Pietrasiewicz
1287222155deSJerry Zhang /* Wait for endpoint to be enabled */
1288222155deSJerry Zhang ep = epfile->ep;
1289222155deSJerry Zhang if (!ep) {
1290222155deSJerry Zhang if (file->f_flags & O_NONBLOCK)
1291222155deSJerry Zhang return -EAGAIN;
1292222155deSJerry Zhang
1293e16828cfSJerry Zhang ret = wait_event_interruptible(
1294e16828cfSJerry Zhang epfile->ffs->wait, (ep = epfile->ep));
1295222155deSJerry Zhang if (ret)
1296222155deSJerry Zhang return -EINTR;
1297222155deSJerry Zhang }
1298222155deSJerry Zhang
129900a2430fSAndrzej Pietrasiewicz spin_lock_irq(&epfile->ffs->eps_lock);
1300222155deSJerry Zhang
1301222155deSJerry Zhang /* In the meantime, endpoint got disabled or changed. */
1302222155deSJerry Zhang if (epfile->ep != ep) {
1303222155deSJerry Zhang spin_unlock_irq(&epfile->ffs->eps_lock);
1304222155deSJerry Zhang return -ESHUTDOWN;
1305222155deSJerry Zhang }
1306222155deSJerry Zhang
130700a2430fSAndrzej Pietrasiewicz switch (code) {
130800a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_FIFO_STATUS:
130900a2430fSAndrzej Pietrasiewicz ret = usb_ep_fifo_status(epfile->ep->ep);
131000a2430fSAndrzej Pietrasiewicz break;
131100a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_FIFO_FLUSH:
131200a2430fSAndrzej Pietrasiewicz usb_ep_fifo_flush(epfile->ep->ep);
131300a2430fSAndrzej Pietrasiewicz ret = 0;
131400a2430fSAndrzej Pietrasiewicz break;
131500a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_CLEAR_HALT:
131600a2430fSAndrzej Pietrasiewicz ret = usb_ep_clear_halt(epfile->ep->ep);
131700a2430fSAndrzej Pietrasiewicz break;
131800a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_ENDPOINT_REVMAP:
131900a2430fSAndrzej Pietrasiewicz ret = epfile->ep->num;
132000a2430fSAndrzej Pietrasiewicz break;
1321c559a353SRobert Baldyga case FUNCTIONFS_ENDPOINT_DESC:
1322c559a353SRobert Baldyga {
1323c559a353SRobert Baldyga int desc_idx;
1324a4b98a75SVamsi Krishna Samavedam struct usb_endpoint_descriptor desc1, *desc;
1325c559a353SRobert Baldyga
1326c559a353SRobert Baldyga switch (epfile->ffs->gadget->speed) {
1327c559a353SRobert Baldyga case USB_SPEED_SUPER:
1328a353397bSJack Pham case USB_SPEED_SUPER_PLUS:
1329c559a353SRobert Baldyga desc_idx = 2;
1330c559a353SRobert Baldyga break;
1331c559a353SRobert Baldyga case USB_SPEED_HIGH:
1332c559a353SRobert Baldyga desc_idx = 1;
1333c559a353SRobert Baldyga break;
1334c559a353SRobert Baldyga default:
1335c559a353SRobert Baldyga desc_idx = 0;
1336c559a353SRobert Baldyga }
1337a4b98a75SVamsi Krishna Samavedam
1338c559a353SRobert Baldyga desc = epfile->ep->descs[desc_idx];
1339a4b98a75SVamsi Krishna Samavedam memcpy(&desc1, desc, desc->bLength);
1340c559a353SRobert Baldyga
1341c559a353SRobert Baldyga spin_unlock_irq(&epfile->ffs->eps_lock);
1342a4b98a75SVamsi Krishna Samavedam ret = copy_to_user((void __user *)value, &desc1, desc1.bLength);
1343c559a353SRobert Baldyga if (ret)
1344c559a353SRobert Baldyga ret = -EFAULT;
1345c559a353SRobert Baldyga return ret;
1346c559a353SRobert Baldyga }
134700a2430fSAndrzej Pietrasiewicz default:
134800a2430fSAndrzej Pietrasiewicz ret = -ENOTTY;
134900a2430fSAndrzej Pietrasiewicz }
135000a2430fSAndrzej Pietrasiewicz spin_unlock_irq(&epfile->ffs->eps_lock);
135100a2430fSAndrzej Pietrasiewicz
135200a2430fSAndrzej Pietrasiewicz return ret;
135300a2430fSAndrzej Pietrasiewicz }
135400a2430fSAndrzej Pietrasiewicz
135500a2430fSAndrzej Pietrasiewicz static const struct file_operations ffs_epfile_operations = {
135600a2430fSAndrzej Pietrasiewicz .llseek = no_llseek,
135700a2430fSAndrzej Pietrasiewicz
135800a2430fSAndrzej Pietrasiewicz .open = ffs_epfile_open,
135970e60d91SAl Viro .write_iter = ffs_epfile_write_iter,
136070e60d91SAl Viro .read_iter = ffs_epfile_read_iter,
136100a2430fSAndrzej Pietrasiewicz .release = ffs_epfile_release,
136200a2430fSAndrzej Pietrasiewicz .unlocked_ioctl = ffs_epfile_ioctl,
136301b8bca8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl,
136400a2430fSAndrzej Pietrasiewicz };
136500a2430fSAndrzej Pietrasiewicz
136600a2430fSAndrzej Pietrasiewicz
136700a2430fSAndrzej Pietrasiewicz /* File system and super block operations ***********************************/
136800a2430fSAndrzej Pietrasiewicz
136900a2430fSAndrzej Pietrasiewicz /*
137000a2430fSAndrzej Pietrasiewicz * Mounting the file system creates a controller file, used first for
137100a2430fSAndrzej Pietrasiewicz * function configuration then later for event monitoring.
137200a2430fSAndrzej Pietrasiewicz */
137300a2430fSAndrzej Pietrasiewicz
137400a2430fSAndrzej Pietrasiewicz static struct inode *__must_check
ffs_sb_make_inode(struct super_block * sb,void * data,const struct file_operations * fops,const struct inode_operations * iops,struct ffs_file_perms * perms)137500a2430fSAndrzej Pietrasiewicz ffs_sb_make_inode(struct super_block *sb, void *data,
137600a2430fSAndrzej Pietrasiewicz const struct file_operations *fops,
137700a2430fSAndrzej Pietrasiewicz const struct inode_operations *iops,
137800a2430fSAndrzej Pietrasiewicz struct ffs_file_perms *perms)
137900a2430fSAndrzej Pietrasiewicz {
138000a2430fSAndrzej Pietrasiewicz struct inode *inode;
138100a2430fSAndrzej Pietrasiewicz
138200a2430fSAndrzej Pietrasiewicz inode = new_inode(sb);
138300a2430fSAndrzej Pietrasiewicz
13848704fd73SGreg Kroah-Hartman if (inode) {
1385c7603adcSJeff Layton struct timespec64 ts = inode_set_ctime_current(inode);
138600a2430fSAndrzej Pietrasiewicz
138700a2430fSAndrzej Pietrasiewicz inode->i_ino = get_next_ino();
138800a2430fSAndrzej Pietrasiewicz inode->i_mode = perms->mode;
138900a2430fSAndrzej Pietrasiewicz inode->i_uid = perms->uid;
139000a2430fSAndrzej Pietrasiewicz inode->i_gid = perms->gid;
1391078cd827SDeepa Dinamani inode->i_atime = ts;
1392078cd827SDeepa Dinamani inode->i_mtime = ts;
139300a2430fSAndrzej Pietrasiewicz inode->i_private = data;
139400a2430fSAndrzej Pietrasiewicz if (fops)
139500a2430fSAndrzej Pietrasiewicz inode->i_fop = fops;
139600a2430fSAndrzej Pietrasiewicz if (iops)
139700a2430fSAndrzej Pietrasiewicz inode->i_op = iops;
139800a2430fSAndrzej Pietrasiewicz }
139900a2430fSAndrzej Pietrasiewicz
140000a2430fSAndrzej Pietrasiewicz return inode;
140100a2430fSAndrzej Pietrasiewicz }
140200a2430fSAndrzej Pietrasiewicz
140300a2430fSAndrzej Pietrasiewicz /* Create "regular" file */
ffs_sb_create_file(struct super_block * sb,const char * name,void * data,const struct file_operations * fops)14041bb27cacSAl Viro static struct dentry *ffs_sb_create_file(struct super_block *sb,
140500a2430fSAndrzej Pietrasiewicz const char *name, void *data,
14061bb27cacSAl Viro const struct file_operations *fops)
140700a2430fSAndrzej Pietrasiewicz {
140800a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = sb->s_fs_info;
140900a2430fSAndrzej Pietrasiewicz struct dentry *dentry;
141000a2430fSAndrzej Pietrasiewicz struct inode *inode;
141100a2430fSAndrzej Pietrasiewicz
141200a2430fSAndrzej Pietrasiewicz dentry = d_alloc_name(sb->s_root, name);
14138704fd73SGreg Kroah-Hartman if (!dentry)
141400a2430fSAndrzej Pietrasiewicz return NULL;
141500a2430fSAndrzej Pietrasiewicz
141600a2430fSAndrzej Pietrasiewicz inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
14178704fd73SGreg Kroah-Hartman if (!inode) {
141800a2430fSAndrzej Pietrasiewicz dput(dentry);
141900a2430fSAndrzej Pietrasiewicz return NULL;
142000a2430fSAndrzej Pietrasiewicz }
142100a2430fSAndrzej Pietrasiewicz
142200a2430fSAndrzej Pietrasiewicz d_add(dentry, inode);
14231bb27cacSAl Viro return dentry;
142400a2430fSAndrzej Pietrasiewicz }
142500a2430fSAndrzej Pietrasiewicz
142600a2430fSAndrzej Pietrasiewicz /* Super block */
142700a2430fSAndrzej Pietrasiewicz static const struct super_operations ffs_sb_operations = {
142800a2430fSAndrzej Pietrasiewicz .statfs = simple_statfs,
142900a2430fSAndrzej Pietrasiewicz .drop_inode = generic_delete_inode,
143000a2430fSAndrzej Pietrasiewicz };
143100a2430fSAndrzej Pietrasiewicz
143200a2430fSAndrzej Pietrasiewicz struct ffs_sb_fill_data {
143300a2430fSAndrzej Pietrasiewicz struct ffs_file_perms perms;
143400a2430fSAndrzej Pietrasiewicz umode_t root_mode;
143500a2430fSAndrzej Pietrasiewicz const char *dev_name;
143618d6b32fSRobert Baldyga bool no_disconnect;
143700a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs_data;
143800a2430fSAndrzej Pietrasiewicz };
143900a2430fSAndrzej Pietrasiewicz
ffs_sb_fill(struct super_block * sb,struct fs_context * fc)1440dec90f61SDavid Howells static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
144100a2430fSAndrzej Pietrasiewicz {
1442dec90f61SDavid Howells struct ffs_sb_fill_data *data = fc->fs_private;
144300a2430fSAndrzej Pietrasiewicz struct inode *inode;
144400a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = data->ffs_data;
144500a2430fSAndrzej Pietrasiewicz
144600a2430fSAndrzej Pietrasiewicz ffs->sb = sb;
144700a2430fSAndrzej Pietrasiewicz data->ffs_data = NULL;
144800a2430fSAndrzej Pietrasiewicz sb->s_fs_info = ffs;
144909cbfeafSKirill A. Shutemov sb->s_blocksize = PAGE_SIZE;
145009cbfeafSKirill A. Shutemov sb->s_blocksize_bits = PAGE_SHIFT;
145100a2430fSAndrzej Pietrasiewicz sb->s_magic = FUNCTIONFS_MAGIC;
145200a2430fSAndrzej Pietrasiewicz sb->s_op = &ffs_sb_operations;
145300a2430fSAndrzej Pietrasiewicz sb->s_time_gran = 1;
145400a2430fSAndrzej Pietrasiewicz
145500a2430fSAndrzej Pietrasiewicz /* Root inode */
145600a2430fSAndrzej Pietrasiewicz data->perms.mode = data->root_mode;
145700a2430fSAndrzej Pietrasiewicz inode = ffs_sb_make_inode(sb, NULL,
145800a2430fSAndrzej Pietrasiewicz &simple_dir_operations,
145900a2430fSAndrzej Pietrasiewicz &simple_dir_inode_operations,
146000a2430fSAndrzej Pietrasiewicz &data->perms);
146100a2430fSAndrzej Pietrasiewicz sb->s_root = d_make_root(inode);
14628704fd73SGreg Kroah-Hartman if (!sb->s_root)
146300a2430fSAndrzej Pietrasiewicz return -ENOMEM;
146400a2430fSAndrzej Pietrasiewicz
146500a2430fSAndrzej Pietrasiewicz /* EP0 file */
14668704fd73SGreg Kroah-Hartman if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations))
146700a2430fSAndrzej Pietrasiewicz return -ENOMEM;
146800a2430fSAndrzej Pietrasiewicz
146900a2430fSAndrzej Pietrasiewicz return 0;
147000a2430fSAndrzej Pietrasiewicz }
147100a2430fSAndrzej Pietrasiewicz
1472dec90f61SDavid Howells enum {
1473dec90f61SDavid Howells Opt_no_disconnect,
1474dec90f61SDavid Howells Opt_rmode,
1475dec90f61SDavid Howells Opt_fmode,
1476dec90f61SDavid Howells Opt_mode,
1477dec90f61SDavid Howells Opt_uid,
1478dec90f61SDavid Howells Opt_gid,
1479dec90f61SDavid Howells };
1480dec90f61SDavid Howells
1481d7167b14SAl Viro static const struct fs_parameter_spec ffs_fs_fs_parameters[] = {
1482dec90f61SDavid Howells fsparam_bool ("no_disconnect", Opt_no_disconnect),
1483dec90f61SDavid Howells fsparam_u32 ("rmode", Opt_rmode),
1484dec90f61SDavid Howells fsparam_u32 ("fmode", Opt_fmode),
1485dec90f61SDavid Howells fsparam_u32 ("mode", Opt_mode),
1486dec90f61SDavid Howells fsparam_u32 ("uid", Opt_uid),
1487dec90f61SDavid Howells fsparam_u32 ("gid", Opt_gid),
1488dec90f61SDavid Howells {}
1489dec90f61SDavid Howells };
1490dec90f61SDavid Howells
ffs_fs_parse_param(struct fs_context * fc,struct fs_parameter * param)1491dec90f61SDavid Howells static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
149200a2430fSAndrzej Pietrasiewicz {
1493dec90f61SDavid Howells struct ffs_sb_fill_data *data = fc->fs_private;
1494dec90f61SDavid Howells struct fs_parse_result result;
1495dec90f61SDavid Howells int opt;
1496dec90f61SDavid Howells
1497d7167b14SAl Viro opt = fs_parse(fc, ffs_fs_fs_parameters, param, &result);
1498dec90f61SDavid Howells if (opt < 0)
1499dec90f61SDavid Howells return opt;
150000a2430fSAndrzej Pietrasiewicz
1501dec90f61SDavid Howells switch (opt) {
1502dec90f61SDavid Howells case Opt_no_disconnect:
1503dec90f61SDavid Howells data->no_disconnect = result.boolean;
150418d6b32fSRobert Baldyga break;
1505dec90f61SDavid Howells case Opt_rmode:
1506dec90f61SDavid Howells data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
1507dec90f61SDavid Howells break;
1508dec90f61SDavid Howells case Opt_fmode:
1509dec90f61SDavid Howells data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
1510dec90f61SDavid Howells break;
1511dec90f61SDavid Howells case Opt_mode:
1512dec90f61SDavid Howells data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
1513dec90f61SDavid Howells data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
151400a2430fSAndrzej Pietrasiewicz break;
151500a2430fSAndrzej Pietrasiewicz
1516dec90f61SDavid Howells case Opt_uid:
1517dec90f61SDavid Howells data->perms.uid = make_kuid(current_user_ns(), result.uint_32);
1518dec90f61SDavid Howells if (!uid_valid(data->perms.uid))
1519dec90f61SDavid Howells goto unmapped_value;
152000a2430fSAndrzej Pietrasiewicz break;
1521dec90f61SDavid Howells case Opt_gid:
1522dec90f61SDavid Howells data->perms.gid = make_kgid(current_user_ns(), result.uint_32);
1523dec90f61SDavid Howells if (!gid_valid(data->perms.gid))
1524dec90f61SDavid Howells goto unmapped_value;
152500a2430fSAndrzej Pietrasiewicz break;
152600a2430fSAndrzej Pietrasiewicz
152700a2430fSAndrzej Pietrasiewicz default:
1528dec90f61SDavid Howells return -ENOPARAM;
152900a2430fSAndrzej Pietrasiewicz }
153000a2430fSAndrzej Pietrasiewicz
153100a2430fSAndrzej Pietrasiewicz return 0;
1532dec90f61SDavid Howells
1533dec90f61SDavid Howells unmapped_value:
1534dec90f61SDavid Howells return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32);
153500a2430fSAndrzej Pietrasiewicz }
153600a2430fSAndrzej Pietrasiewicz
1537dec90f61SDavid Howells /*
1538dec90f61SDavid Howells * Set up the superblock for a mount.
1539dec90f61SDavid Howells */
ffs_fs_get_tree(struct fs_context * fc)1540dec90f61SDavid Howells static int ffs_fs_get_tree(struct fs_context *fc)
154100a2430fSAndrzej Pietrasiewicz {
1542dec90f61SDavid Howells struct ffs_sb_fill_data *ctx = fc->fs_private;
154300a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs;
1544ecfbd7b9SAndrew Gabbasov int ret;
154500a2430fSAndrzej Pietrasiewicz
1546dec90f61SDavid Howells if (!fc->source)
1547dec90f61SDavid Howells return invalf(fc, "No source specified");
154800a2430fSAndrzej Pietrasiewicz
1549dec90f61SDavid Howells ffs = ffs_data_new(fc->source);
15508704fd73SGreg Kroah-Hartman if (!ffs)
1551dec90f61SDavid Howells return -ENOMEM;
1552dec90f61SDavid Howells ffs->file_perms = ctx->perms;
1553dec90f61SDavid Howells ffs->no_disconnect = ctx->no_disconnect;
155400a2430fSAndrzej Pietrasiewicz
1555dec90f61SDavid Howells ffs->dev_name = kstrdup(fc->source, GFP_KERNEL);
15568704fd73SGreg Kroah-Hartman if (!ffs->dev_name) {
155700a2430fSAndrzej Pietrasiewicz ffs_data_put(ffs);
1558dec90f61SDavid Howells return -ENOMEM;
155900a2430fSAndrzej Pietrasiewicz }
156000a2430fSAndrzej Pietrasiewicz
1561ecfbd7b9SAndrew Gabbasov ret = ffs_acquire_dev(ffs->dev_name, ffs);
1562ecfbd7b9SAndrew Gabbasov if (ret) {
156300a2430fSAndrzej Pietrasiewicz ffs_data_put(ffs);
1564ecfbd7b9SAndrew Gabbasov return ret;
156500a2430fSAndrzej Pietrasiewicz }
156600a2430fSAndrzej Pietrasiewicz
1567dec90f61SDavid Howells ctx->ffs_data = ffs;
1568dec90f61SDavid Howells return get_tree_nodev(fc, ffs_sb_fill);
156900a2430fSAndrzej Pietrasiewicz }
1570dec90f61SDavid Howells
ffs_fs_free_fc(struct fs_context * fc)1571dec90f61SDavid Howells static void ffs_fs_free_fc(struct fs_context *fc)
1572dec90f61SDavid Howells {
1573dec90f61SDavid Howells struct ffs_sb_fill_data *ctx = fc->fs_private;
1574dec90f61SDavid Howells
1575dec90f61SDavid Howells if (ctx) {
1576dec90f61SDavid Howells if (ctx->ffs_data) {
1577dec90f61SDavid Howells ffs_data_put(ctx->ffs_data);
1578dec90f61SDavid Howells }
1579dec90f61SDavid Howells
1580dec90f61SDavid Howells kfree(ctx);
1581dec90f61SDavid Howells }
1582dec90f61SDavid Howells }
1583dec90f61SDavid Howells
1584dec90f61SDavid Howells static const struct fs_context_operations ffs_fs_context_ops = {
1585dec90f61SDavid Howells .free = ffs_fs_free_fc,
1586dec90f61SDavid Howells .parse_param = ffs_fs_parse_param,
1587dec90f61SDavid Howells .get_tree = ffs_fs_get_tree,
1588dec90f61SDavid Howells };
1589dec90f61SDavid Howells
ffs_fs_init_fs_context(struct fs_context * fc)1590dec90f61SDavid Howells static int ffs_fs_init_fs_context(struct fs_context *fc)
1591dec90f61SDavid Howells {
1592dec90f61SDavid Howells struct ffs_sb_fill_data *ctx;
1593dec90f61SDavid Howells
1594dec90f61SDavid Howells ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL);
1595dec90f61SDavid Howells if (!ctx)
1596dec90f61SDavid Howells return -ENOMEM;
1597dec90f61SDavid Howells
1598dec90f61SDavid Howells ctx->perms.mode = S_IFREG | 0600;
1599dec90f61SDavid Howells ctx->perms.uid = GLOBAL_ROOT_UID;
1600dec90f61SDavid Howells ctx->perms.gid = GLOBAL_ROOT_GID;
1601dec90f61SDavid Howells ctx->root_mode = S_IFDIR | 0500;
1602dec90f61SDavid Howells ctx->no_disconnect = false;
1603dec90f61SDavid Howells
1604dec90f61SDavid Howells fc->fs_private = ctx;
1605dec90f61SDavid Howells fc->ops = &ffs_fs_context_ops;
1606dec90f61SDavid Howells return 0;
160700a2430fSAndrzej Pietrasiewicz }
160800a2430fSAndrzej Pietrasiewicz
160900a2430fSAndrzej Pietrasiewicz static void
ffs_fs_kill_sb(struct super_block * sb)161000a2430fSAndrzej Pietrasiewicz ffs_fs_kill_sb(struct super_block *sb)
161100a2430fSAndrzej Pietrasiewicz {
161200a2430fSAndrzej Pietrasiewicz kill_litter_super(sb);
1613ecfbd7b9SAndrew Gabbasov if (sb->s_fs_info)
161418d6b32fSRobert Baldyga ffs_data_closed(sb->s_fs_info);
161500a2430fSAndrzej Pietrasiewicz }
161600a2430fSAndrzej Pietrasiewicz
161700a2430fSAndrzej Pietrasiewicz static struct file_system_type ffs_fs_type = {
161800a2430fSAndrzej Pietrasiewicz .owner = THIS_MODULE,
161900a2430fSAndrzej Pietrasiewicz .name = "functionfs",
1620dec90f61SDavid Howells .init_fs_context = ffs_fs_init_fs_context,
1621d7167b14SAl Viro .parameters = ffs_fs_fs_parameters,
162200a2430fSAndrzej Pietrasiewicz .kill_sb = ffs_fs_kill_sb,
162300a2430fSAndrzej Pietrasiewicz };
162400a2430fSAndrzej Pietrasiewicz MODULE_ALIAS_FS("functionfs");
162500a2430fSAndrzej Pietrasiewicz
162600a2430fSAndrzej Pietrasiewicz
162700a2430fSAndrzej Pietrasiewicz /* Driver's main init/cleanup functions *************************************/
162800a2430fSAndrzej Pietrasiewicz
functionfs_init(void)162900a2430fSAndrzej Pietrasiewicz static int functionfs_init(void)
163000a2430fSAndrzej Pietrasiewicz {
163100a2430fSAndrzej Pietrasiewicz int ret;
163200a2430fSAndrzej Pietrasiewicz
163300a2430fSAndrzej Pietrasiewicz ret = register_filesystem(&ffs_fs_type);
16348704fd73SGreg Kroah-Hartman if (!ret)
163500a2430fSAndrzej Pietrasiewicz pr_info("file system registered\n");
163600a2430fSAndrzej Pietrasiewicz else
163700a2430fSAndrzej Pietrasiewicz pr_err("failed registering file system (%d)\n", ret);
163800a2430fSAndrzej Pietrasiewicz
163900a2430fSAndrzej Pietrasiewicz return ret;
164000a2430fSAndrzej Pietrasiewicz }
164100a2430fSAndrzej Pietrasiewicz
functionfs_cleanup(void)164200a2430fSAndrzej Pietrasiewicz static void functionfs_cleanup(void)
164300a2430fSAndrzej Pietrasiewicz {
164400a2430fSAndrzej Pietrasiewicz pr_info("unloading\n");
164500a2430fSAndrzej Pietrasiewicz unregister_filesystem(&ffs_fs_type);
164600a2430fSAndrzej Pietrasiewicz }
164700a2430fSAndrzej Pietrasiewicz
164800a2430fSAndrzej Pietrasiewicz
164900a2430fSAndrzej Pietrasiewicz /* ffs_data and ffs_function construction and destruction code **************/
165000a2430fSAndrzej Pietrasiewicz
165100a2430fSAndrzej Pietrasiewicz static void ffs_data_clear(struct ffs_data *ffs);
165200a2430fSAndrzej Pietrasiewicz static void ffs_data_reset(struct ffs_data *ffs);
165300a2430fSAndrzej Pietrasiewicz
ffs_data_get(struct ffs_data * ffs)165400a2430fSAndrzej Pietrasiewicz static void ffs_data_get(struct ffs_data *ffs)
165500a2430fSAndrzej Pietrasiewicz {
165643938613SElena Reshetova refcount_inc(&ffs->ref);
165700a2430fSAndrzej Pietrasiewicz }
165800a2430fSAndrzej Pietrasiewicz
ffs_data_opened(struct ffs_data * ffs)165900a2430fSAndrzej Pietrasiewicz static void ffs_data_opened(struct ffs_data *ffs)
166000a2430fSAndrzej Pietrasiewicz {
166143938613SElena Reshetova refcount_inc(&ffs->ref);
166218d6b32fSRobert Baldyga if (atomic_add_return(1, &ffs->opened) == 1 &&
166318d6b32fSRobert Baldyga ffs->state == FFS_DEACTIVATED) {
166418d6b32fSRobert Baldyga ffs->state = FFS_CLOSING;
166518d6b32fSRobert Baldyga ffs_data_reset(ffs);
166618d6b32fSRobert Baldyga }
166700a2430fSAndrzej Pietrasiewicz }
166800a2430fSAndrzej Pietrasiewicz
ffs_data_put(struct ffs_data * ffs)166900a2430fSAndrzej Pietrasiewicz static void ffs_data_put(struct ffs_data *ffs)
167000a2430fSAndrzej Pietrasiewicz {
16718704fd73SGreg Kroah-Hartman if (refcount_dec_and_test(&ffs->ref)) {
167200a2430fSAndrzej Pietrasiewicz pr_info("%s(): freeing\n", __func__);
167300a2430fSAndrzej Pietrasiewicz ffs_data_clear(ffs);
1674ecfbd7b9SAndrew Gabbasov ffs_release_dev(ffs->private_data);
167500a2430fSAndrzej Pietrasiewicz BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
1676a5c6234eSThomas Gleixner swait_active(&ffs->ep0req_completion.wait) ||
1677e16828cfSJerry Zhang waitqueue_active(&ffs->wait));
1678addfc582SJohn Keeping destroy_workqueue(ffs->io_completion_wq);
167900a2430fSAndrzej Pietrasiewicz kfree(ffs->dev_name);
168000a2430fSAndrzej Pietrasiewicz kfree(ffs);
168100a2430fSAndrzej Pietrasiewicz }
168200a2430fSAndrzej Pietrasiewicz }
168300a2430fSAndrzej Pietrasiewicz
ffs_data_closed(struct ffs_data * ffs)168400a2430fSAndrzej Pietrasiewicz static void ffs_data_closed(struct ffs_data *ffs)
168500a2430fSAndrzej Pietrasiewicz {
1686ebe2b1adSUdipto Goswami struct ffs_epfile *epfiles;
1687ebe2b1adSUdipto Goswami unsigned long flags;
1688ebe2b1adSUdipto Goswami
168900a2430fSAndrzej Pietrasiewicz if (atomic_dec_and_test(&ffs->opened)) {
169018d6b32fSRobert Baldyga if (ffs->no_disconnect) {
169118d6b32fSRobert Baldyga ffs->state = FFS_DEACTIVATED;
1692ebe2b1adSUdipto Goswami spin_lock_irqsave(&ffs->eps_lock, flags);
1693ebe2b1adSUdipto Goswami epfiles = ffs->epfiles;
169418d6b32fSRobert Baldyga ffs->epfiles = NULL;
1695ebe2b1adSUdipto Goswami spin_unlock_irqrestore(&ffs->eps_lock,
1696ebe2b1adSUdipto Goswami flags);
1697ebe2b1adSUdipto Goswami
1698ebe2b1adSUdipto Goswami if (epfiles)
1699ebe2b1adSUdipto Goswami ffs_epfiles_destroy(epfiles,
1700ebe2b1adSUdipto Goswami ffs->eps_count);
1701ebe2b1adSUdipto Goswami
170218d6b32fSRobert Baldyga if (ffs->setup_state == FFS_SETUP_PENDING)
170318d6b32fSRobert Baldyga __ffs_ep0_stall(ffs);
170418d6b32fSRobert Baldyga } else {
170518d6b32fSRobert Baldyga ffs->state = FFS_CLOSING;
170618d6b32fSRobert Baldyga ffs_data_reset(ffs);
170718d6b32fSRobert Baldyga }
170818d6b32fSRobert Baldyga }
170918d6b32fSRobert Baldyga if (atomic_read(&ffs->opened) < 0) {
171000a2430fSAndrzej Pietrasiewicz ffs->state = FFS_CLOSING;
171100a2430fSAndrzej Pietrasiewicz ffs_data_reset(ffs);
171200a2430fSAndrzej Pietrasiewicz }
171300a2430fSAndrzej Pietrasiewicz
171400a2430fSAndrzej Pietrasiewicz ffs_data_put(ffs);
171500a2430fSAndrzej Pietrasiewicz }
171600a2430fSAndrzej Pietrasiewicz
ffs_data_new(const char * dev_name)1717addfc582SJohn Keeping static struct ffs_data *ffs_data_new(const char *dev_name)
171800a2430fSAndrzej Pietrasiewicz {
171900a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
17208704fd73SGreg Kroah-Hartman if (!ffs)
172100a2430fSAndrzej Pietrasiewicz return NULL;
172200a2430fSAndrzej Pietrasiewicz
1723addfc582SJohn Keeping ffs->io_completion_wq = alloc_ordered_workqueue("%s", 0, dev_name);
1724addfc582SJohn Keeping if (!ffs->io_completion_wq) {
1725addfc582SJohn Keeping kfree(ffs);
1726addfc582SJohn Keeping return NULL;
1727addfc582SJohn Keeping }
1728addfc582SJohn Keeping
172943938613SElena Reshetova refcount_set(&ffs->ref, 1);
173000a2430fSAndrzej Pietrasiewicz atomic_set(&ffs->opened, 0);
173100a2430fSAndrzej Pietrasiewicz ffs->state = FFS_READ_DESCRIPTORS;
173200a2430fSAndrzej Pietrasiewicz mutex_init(&ffs->mutex);
173300a2430fSAndrzej Pietrasiewicz spin_lock_init(&ffs->eps_lock);
173400a2430fSAndrzej Pietrasiewicz init_waitqueue_head(&ffs->ev.waitq);
1735e16828cfSJerry Zhang init_waitqueue_head(&ffs->wait);
173600a2430fSAndrzej Pietrasiewicz init_completion(&ffs->ep0req_completion);
173700a2430fSAndrzej Pietrasiewicz
173800a2430fSAndrzej Pietrasiewicz /* XXX REVISIT need to update it in some places, or do we? */
173900a2430fSAndrzej Pietrasiewicz ffs->ev.can_stall = 1;
174000a2430fSAndrzej Pietrasiewicz
174100a2430fSAndrzej Pietrasiewicz return ffs;
174200a2430fSAndrzej Pietrasiewicz }
174300a2430fSAndrzej Pietrasiewicz
ffs_data_clear(struct ffs_data * ffs)174400a2430fSAndrzej Pietrasiewicz static void ffs_data_clear(struct ffs_data *ffs)
174500a2430fSAndrzej Pietrasiewicz {
1746ebe2b1adSUdipto Goswami struct ffs_epfile *epfiles;
1747ebe2b1adSUdipto Goswami unsigned long flags;
1748ebe2b1adSUdipto Goswami
174900a2430fSAndrzej Pietrasiewicz ffs_closed(ffs);
175000a2430fSAndrzej Pietrasiewicz
175100a2430fSAndrzej Pietrasiewicz BUG_ON(ffs->gadget);
175200a2430fSAndrzej Pietrasiewicz
1753ebe2b1adSUdipto Goswami spin_lock_irqsave(&ffs->eps_lock, flags);
1754ebe2b1adSUdipto Goswami epfiles = ffs->epfiles;
1755ebe2b1adSUdipto Goswami ffs->epfiles = NULL;
1756ebe2b1adSUdipto Goswami spin_unlock_irqrestore(&ffs->eps_lock, flags);
1757ebe2b1adSUdipto Goswami
1758ebe2b1adSUdipto Goswami /*
1759ebe2b1adSUdipto Goswami * potential race possible between ffs_func_eps_disable
1760ebe2b1adSUdipto Goswami * & ffs_epfile_release therefore maintaining a local
1761ebe2b1adSUdipto Goswami * copy of epfile will save us from use-after-free.
1762ebe2b1adSUdipto Goswami */
1763ebe2b1adSUdipto Goswami if (epfiles) {
1764ebe2b1adSUdipto Goswami ffs_epfiles_destroy(epfiles, ffs->eps_count);
1765b1e08873SVincent Pelletier ffs->epfiles = NULL;
1766b1e08873SVincent Pelletier }
176700a2430fSAndrzej Pietrasiewicz
1768b1e08873SVincent Pelletier if (ffs->ffs_eventfd) {
17695e33f6fdSRobert Baldyga eventfd_ctx_put(ffs->ffs_eventfd);
1770b1e08873SVincent Pelletier ffs->ffs_eventfd = NULL;
1771b1e08873SVincent Pelletier }
17725e33f6fdSRobert Baldyga
177300a2430fSAndrzej Pietrasiewicz kfree(ffs->raw_descs_data);
177400a2430fSAndrzej Pietrasiewicz kfree(ffs->raw_strings);
177500a2430fSAndrzej Pietrasiewicz kfree(ffs->stringtabs);
177600a2430fSAndrzej Pietrasiewicz }
177700a2430fSAndrzej Pietrasiewicz
ffs_data_reset(struct ffs_data * ffs)177800a2430fSAndrzej Pietrasiewicz static void ffs_data_reset(struct ffs_data *ffs)
177900a2430fSAndrzej Pietrasiewicz {
178000a2430fSAndrzej Pietrasiewicz ffs_data_clear(ffs);
178100a2430fSAndrzej Pietrasiewicz
178200a2430fSAndrzej Pietrasiewicz ffs->raw_descs_data = NULL;
178300a2430fSAndrzej Pietrasiewicz ffs->raw_descs = NULL;
178400a2430fSAndrzej Pietrasiewicz ffs->raw_strings = NULL;
178500a2430fSAndrzej Pietrasiewicz ffs->stringtabs = NULL;
178600a2430fSAndrzej Pietrasiewicz
178700a2430fSAndrzej Pietrasiewicz ffs->raw_descs_length = 0;
178800a2430fSAndrzej Pietrasiewicz ffs->fs_descs_count = 0;
178900a2430fSAndrzej Pietrasiewicz ffs->hs_descs_count = 0;
179000a2430fSAndrzej Pietrasiewicz ffs->ss_descs_count = 0;
179100a2430fSAndrzej Pietrasiewicz
179200a2430fSAndrzej Pietrasiewicz ffs->strings_count = 0;
179300a2430fSAndrzej Pietrasiewicz ffs->interfaces_count = 0;
179400a2430fSAndrzej Pietrasiewicz ffs->eps_count = 0;
179500a2430fSAndrzej Pietrasiewicz
179600a2430fSAndrzej Pietrasiewicz ffs->ev.count = 0;
179700a2430fSAndrzej Pietrasiewicz
179800a2430fSAndrzej Pietrasiewicz ffs->state = FFS_READ_DESCRIPTORS;
179900a2430fSAndrzej Pietrasiewicz ffs->setup_state = FFS_NO_SETUP;
180000a2430fSAndrzej Pietrasiewicz ffs->flags = 0;
18011c2e54fbSUdipto Goswami
18021c2e54fbSUdipto Goswami ffs->ms_os_descs_ext_prop_count = 0;
18031c2e54fbSUdipto Goswami ffs->ms_os_descs_ext_prop_name_len = 0;
18041c2e54fbSUdipto Goswami ffs->ms_os_descs_ext_prop_data_len = 0;
180500a2430fSAndrzej Pietrasiewicz }
180600a2430fSAndrzej Pietrasiewicz
180700a2430fSAndrzej Pietrasiewicz
functionfs_bind(struct ffs_data * ffs,struct usb_composite_dev * cdev)180800a2430fSAndrzej Pietrasiewicz static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
180900a2430fSAndrzej Pietrasiewicz {
181000a2430fSAndrzej Pietrasiewicz struct usb_gadget_strings **lang;
181100a2430fSAndrzej Pietrasiewicz int first_id;
181200a2430fSAndrzej Pietrasiewicz
1813*ea6a1498SAkash M if ((ffs->state != FFS_ACTIVE
181400a2430fSAndrzej Pietrasiewicz || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
181500a2430fSAndrzej Pietrasiewicz return -EBADFD;
181600a2430fSAndrzej Pietrasiewicz
181700a2430fSAndrzej Pietrasiewicz first_id = usb_string_ids_n(cdev, ffs->strings_count);
18188704fd73SGreg Kroah-Hartman if (first_id < 0)
181900a2430fSAndrzej Pietrasiewicz return first_id;
182000a2430fSAndrzej Pietrasiewicz
182100a2430fSAndrzej Pietrasiewicz ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
18228704fd73SGreg Kroah-Hartman if (!ffs->ep0req)
182300a2430fSAndrzej Pietrasiewicz return -ENOMEM;
182400a2430fSAndrzej Pietrasiewicz ffs->ep0req->complete = ffs_ep0_complete;
182500a2430fSAndrzej Pietrasiewicz ffs->ep0req->context = ffs;
182600a2430fSAndrzej Pietrasiewicz
182700a2430fSAndrzej Pietrasiewicz lang = ffs->stringtabs;
182861fe2d75SGreg Kroah-Hartman if (lang) {
182961fe2d75SGreg Kroah-Hartman for (; *lang; ++lang) {
183000a2430fSAndrzej Pietrasiewicz struct usb_string *str = (*lang)->strings;
183100a2430fSAndrzej Pietrasiewicz int id = first_id;
183200a2430fSAndrzej Pietrasiewicz for (; str->s; ++id, ++str)
183300a2430fSAndrzej Pietrasiewicz str->id = id;
183400a2430fSAndrzej Pietrasiewicz }
183561fe2d75SGreg Kroah-Hartman }
183600a2430fSAndrzej Pietrasiewicz
183700a2430fSAndrzej Pietrasiewicz ffs->gadget = cdev->gadget;
183800a2430fSAndrzej Pietrasiewicz ffs_data_get(ffs);
183900a2430fSAndrzej Pietrasiewicz return 0;
184000a2430fSAndrzej Pietrasiewicz }
184100a2430fSAndrzej Pietrasiewicz
functionfs_unbind(struct ffs_data * ffs)184200a2430fSAndrzej Pietrasiewicz static void functionfs_unbind(struct ffs_data *ffs)
184300a2430fSAndrzej Pietrasiewicz {
184400a2430fSAndrzej Pietrasiewicz if (!WARN_ON(!ffs->gadget)) {
1845ce405d56SUdipto Goswami /* dequeue before freeing ep0req */
1846ce405d56SUdipto Goswami usb_ep_dequeue(ffs->gadget->ep0, ffs->ep0req);
18476a19da11SUdipto Goswami mutex_lock(&ffs->mutex);
184800a2430fSAndrzej Pietrasiewicz usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
184900a2430fSAndrzej Pietrasiewicz ffs->ep0req = NULL;
185000a2430fSAndrzej Pietrasiewicz ffs->gadget = NULL;
185100a2430fSAndrzej Pietrasiewicz clear_bit(FFS_FL_BOUND, &ffs->flags);
18526a19da11SUdipto Goswami mutex_unlock(&ffs->mutex);
185300a2430fSAndrzej Pietrasiewicz ffs_data_put(ffs);
185400a2430fSAndrzej Pietrasiewicz }
185500a2430fSAndrzej Pietrasiewicz }
185600a2430fSAndrzej Pietrasiewicz
ffs_epfiles_create(struct ffs_data * ffs)185700a2430fSAndrzej Pietrasiewicz static int ffs_epfiles_create(struct ffs_data *ffs)
185800a2430fSAndrzej Pietrasiewicz {
185900a2430fSAndrzej Pietrasiewicz struct ffs_epfile *epfile, *epfiles;
186000a2430fSAndrzej Pietrasiewicz unsigned i, count;
186100a2430fSAndrzej Pietrasiewicz
186200a2430fSAndrzej Pietrasiewicz count = ffs->eps_count;
186300a2430fSAndrzej Pietrasiewicz epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
186400a2430fSAndrzej Pietrasiewicz if (!epfiles)
186500a2430fSAndrzej Pietrasiewicz return -ENOMEM;
186600a2430fSAndrzej Pietrasiewicz
186700a2430fSAndrzej Pietrasiewicz epfile = epfiles;
186800a2430fSAndrzej Pietrasiewicz for (i = 1; i <= count; ++i, ++epfile) {
186900a2430fSAndrzej Pietrasiewicz epfile->ffs = ffs;
187000a2430fSAndrzej Pietrasiewicz mutex_init(&epfile->mutex);
18711b0bf88fSRobert Baldyga if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
1872acba23feSMario Schuknecht sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
18731b0bf88fSRobert Baldyga else
1874acba23feSMario Schuknecht sprintf(epfile->name, "ep%u", i);
1875acba23feSMario Schuknecht epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
18761bb27cacSAl Viro epfile,
18771bb27cacSAl Viro &ffs_epfile_operations);
18788704fd73SGreg Kroah-Hartman if (!epfile->dentry) {
187900a2430fSAndrzej Pietrasiewicz ffs_epfiles_destroy(epfiles, i - 1);
188000a2430fSAndrzej Pietrasiewicz return -ENOMEM;
188100a2430fSAndrzej Pietrasiewicz }
188200a2430fSAndrzej Pietrasiewicz }
188300a2430fSAndrzej Pietrasiewicz
188400a2430fSAndrzej Pietrasiewicz ffs->epfiles = epfiles;
188500a2430fSAndrzej Pietrasiewicz return 0;
188600a2430fSAndrzej Pietrasiewicz }
188700a2430fSAndrzej Pietrasiewicz
ffs_epfiles_destroy(struct ffs_epfile * epfiles,unsigned count)188800a2430fSAndrzej Pietrasiewicz static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
188900a2430fSAndrzej Pietrasiewicz {
189000a2430fSAndrzej Pietrasiewicz struct ffs_epfile *epfile = epfiles;
189100a2430fSAndrzej Pietrasiewicz
189200a2430fSAndrzej Pietrasiewicz for (; count; --count, ++epfile) {
1893e16828cfSJerry Zhang BUG_ON(mutex_is_locked(&epfile->mutex));
189400a2430fSAndrzej Pietrasiewicz if (epfile->dentry) {
189500a2430fSAndrzej Pietrasiewicz d_delete(epfile->dentry);
189600a2430fSAndrzej Pietrasiewicz dput(epfile->dentry);
189700a2430fSAndrzej Pietrasiewicz epfile->dentry = NULL;
189800a2430fSAndrzej Pietrasiewicz }
189900a2430fSAndrzej Pietrasiewicz }
190000a2430fSAndrzej Pietrasiewicz
190100a2430fSAndrzej Pietrasiewicz kfree(epfiles);
190200a2430fSAndrzej Pietrasiewicz }
190300a2430fSAndrzej Pietrasiewicz
ffs_func_eps_disable(struct ffs_function * func)190400a2430fSAndrzej Pietrasiewicz static void ffs_func_eps_disable(struct ffs_function *func)
190500a2430fSAndrzej Pietrasiewicz {
1906ebe2b1adSUdipto Goswami struct ffs_ep *ep;
1907ebe2b1adSUdipto Goswami struct ffs_epfile *epfile;
1908ebe2b1adSUdipto Goswami unsigned short count;
190900a2430fSAndrzej Pietrasiewicz unsigned long flags;
191000a2430fSAndrzej Pietrasiewicz
19119353afbbSMichal Nazarewicz spin_lock_irqsave(&func->ffs->eps_lock, flags);
1912ebe2b1adSUdipto Goswami count = func->ffs->eps_count;
1913ebe2b1adSUdipto Goswami epfile = func->ffs->epfiles;
1914ebe2b1adSUdipto Goswami ep = func->eps;
191508f37148SVincent Pelletier while (count--) {
191600a2430fSAndrzej Pietrasiewicz /* pending requests get nuked */
19178704fd73SGreg Kroah-Hartman if (ep->ep)
191800a2430fSAndrzej Pietrasiewicz usb_ep_disable(ep->ep);
191900a2430fSAndrzej Pietrasiewicz ++ep;
192018d6b32fSRobert Baldyga
192118d6b32fSRobert Baldyga if (epfile) {
1922a9e6f83cSMichal Nazarewicz epfile->ep = NULL;
1923a9e6f83cSMichal Nazarewicz __ffs_epfile_read_buffer_free(epfile);
192400a2430fSAndrzej Pietrasiewicz ++epfile;
192518d6b32fSRobert Baldyga }
192608f37148SVincent Pelletier }
1927a9e6f83cSMichal Nazarewicz spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
192800a2430fSAndrzej Pietrasiewicz }
192900a2430fSAndrzej Pietrasiewicz
ffs_func_eps_enable(struct ffs_function * func)193000a2430fSAndrzej Pietrasiewicz static int ffs_func_eps_enable(struct ffs_function *func)
193100a2430fSAndrzej Pietrasiewicz {
1932ebe2b1adSUdipto Goswami struct ffs_data *ffs;
1933ebe2b1adSUdipto Goswami struct ffs_ep *ep;
1934ebe2b1adSUdipto Goswami struct ffs_epfile *epfile;
1935ebe2b1adSUdipto Goswami unsigned short count;
193600a2430fSAndrzej Pietrasiewicz unsigned long flags;
193700a2430fSAndrzej Pietrasiewicz int ret = 0;
193800a2430fSAndrzej Pietrasiewicz
193900a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&func->ffs->eps_lock, flags);
1940ebe2b1adSUdipto Goswami ffs = func->ffs;
1941ebe2b1adSUdipto Goswami ep = func->eps;
1942ebe2b1adSUdipto Goswami epfile = ffs->epfiles;
1943ebe2b1adSUdipto Goswami count = ffs->eps_count;
194408f37148SVincent Pelletier while(count--) {
194500a2430fSAndrzej Pietrasiewicz ep->ep->driver_data = ep;
19462bfa0719SFelipe Balbi
1947675272d0SJack Pham ret = config_ep_by_speed(func->gadget, &func->function, ep->ep);
1948675272d0SJack Pham if (ret) {
1949675272d0SJack Pham pr_err("%s: config_ep_by_speed(%s) returned %d\n",
1950675272d0SJack Pham __func__, ep->ep->name, ret);
1951675272d0SJack Pham break;
1952b7f73850SWilliam Wu }
19532bfa0719SFelipe Balbi
195400a2430fSAndrzej Pietrasiewicz ret = usb_ep_enable(ep->ep);
19558704fd73SGreg Kroah-Hartman if (!ret) {
195600a2430fSAndrzej Pietrasiewicz epfile->ep = ep;
1957675272d0SJack Pham epfile->in = usb_endpoint_dir_in(ep->ep->desc);
1958675272d0SJack Pham epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc);
195900a2430fSAndrzej Pietrasiewicz } else {
196000a2430fSAndrzej Pietrasiewicz break;
196100a2430fSAndrzej Pietrasiewicz }
196200a2430fSAndrzej Pietrasiewicz
196300a2430fSAndrzej Pietrasiewicz ++ep;
196400a2430fSAndrzej Pietrasiewicz ++epfile;
196508f37148SVincent Pelletier }
1966e16828cfSJerry Zhang
1967e16828cfSJerry Zhang wake_up_interruptible(&ffs->wait);
196800a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
196900a2430fSAndrzej Pietrasiewicz
197000a2430fSAndrzej Pietrasiewicz return ret;
197100a2430fSAndrzej Pietrasiewicz }
197200a2430fSAndrzej Pietrasiewicz
197300a2430fSAndrzej Pietrasiewicz
197400a2430fSAndrzej Pietrasiewicz /* Parsing and building descriptors and strings *****************************/
197500a2430fSAndrzej Pietrasiewicz
197600a2430fSAndrzej Pietrasiewicz /*
197700a2430fSAndrzej Pietrasiewicz * This validates if data pointed by data is a valid USB descriptor as
197800a2430fSAndrzej Pietrasiewicz * well as record how many interfaces, endpoints and strings are
197900a2430fSAndrzej Pietrasiewicz * required by given configuration. Returns address after the
198000a2430fSAndrzej Pietrasiewicz * descriptor or NULL if data is invalid.
198100a2430fSAndrzej Pietrasiewicz */
198200a2430fSAndrzej Pietrasiewicz
198300a2430fSAndrzej Pietrasiewicz enum ffs_entity_type {
198400a2430fSAndrzej Pietrasiewicz FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
198500a2430fSAndrzej Pietrasiewicz };
198600a2430fSAndrzej Pietrasiewicz
198700a2430fSAndrzej Pietrasiewicz enum ffs_os_desc_type {
198800a2430fSAndrzej Pietrasiewicz FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
198900a2430fSAndrzej Pietrasiewicz };
199000a2430fSAndrzej Pietrasiewicz
199100a2430fSAndrzej Pietrasiewicz typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
199200a2430fSAndrzej Pietrasiewicz u8 *valuep,
199300a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header *desc,
199400a2430fSAndrzej Pietrasiewicz void *priv);
199500a2430fSAndrzej Pietrasiewicz
199600a2430fSAndrzej Pietrasiewicz typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
199700a2430fSAndrzej Pietrasiewicz struct usb_os_desc_header *h, void *data,
199800a2430fSAndrzej Pietrasiewicz unsigned len, void *priv);
199900a2430fSAndrzej Pietrasiewicz
ffs_do_single_desc(char * data,unsigned len,ffs_entity_callback entity,void * priv,int * current_class)200000a2430fSAndrzej Pietrasiewicz static int __must_check ffs_do_single_desc(char *data, unsigned len,
200100a2430fSAndrzej Pietrasiewicz ffs_entity_callback entity,
20027f7c548cSVincent Pelletier void *priv, int *current_class)
200300a2430fSAndrzej Pietrasiewicz {
200400a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header *_ds = (void *)data;
200500a2430fSAndrzej Pietrasiewicz u8 length;
200600a2430fSAndrzej Pietrasiewicz int ret;
200700a2430fSAndrzej Pietrasiewicz
200800a2430fSAndrzej Pietrasiewicz /* At least two bytes are required: length and type */
200900a2430fSAndrzej Pietrasiewicz if (len < 2) {
201000a2430fSAndrzej Pietrasiewicz pr_vdebug("descriptor too short\n");
201100a2430fSAndrzej Pietrasiewicz return -EINVAL;
201200a2430fSAndrzej Pietrasiewicz }
201300a2430fSAndrzej Pietrasiewicz
201400a2430fSAndrzej Pietrasiewicz /* If we have at least as many bytes as the descriptor takes? */
201500a2430fSAndrzej Pietrasiewicz length = _ds->bLength;
201600a2430fSAndrzej Pietrasiewicz if (len < length) {
201700a2430fSAndrzej Pietrasiewicz pr_vdebug("descriptor longer then available data\n");
201800a2430fSAndrzej Pietrasiewicz return -EINVAL;
201900a2430fSAndrzej Pietrasiewicz }
202000a2430fSAndrzej Pietrasiewicz
202100a2430fSAndrzej Pietrasiewicz #define __entity_check_INTERFACE(val) 1
202200a2430fSAndrzej Pietrasiewicz #define __entity_check_STRING(val) (val)
202300a2430fSAndrzej Pietrasiewicz #define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK)
202400a2430fSAndrzej Pietrasiewicz #define __entity(type, val) do { \
202500a2430fSAndrzej Pietrasiewicz pr_vdebug("entity " #type "(%02x)\n", (val)); \
20268704fd73SGreg Kroah-Hartman if (!__entity_check_ ##type(val)) { \
202700a2430fSAndrzej Pietrasiewicz pr_vdebug("invalid entity's value\n"); \
202800a2430fSAndrzej Pietrasiewicz return -EINVAL; \
202900a2430fSAndrzej Pietrasiewicz } \
203000a2430fSAndrzej Pietrasiewicz ret = entity(FFS_ ##type, &val, _ds, priv); \
20318704fd73SGreg Kroah-Hartman if (ret < 0) { \
203200a2430fSAndrzej Pietrasiewicz pr_debug("entity " #type "(%02x); ret = %d\n", \
203300a2430fSAndrzej Pietrasiewicz (val), ret); \
203400a2430fSAndrzej Pietrasiewicz return ret; \
203500a2430fSAndrzej Pietrasiewicz } \
203600a2430fSAndrzej Pietrasiewicz } while (0)
203700a2430fSAndrzej Pietrasiewicz
203800a2430fSAndrzej Pietrasiewicz /* Parse descriptor depending on type. */
203900a2430fSAndrzej Pietrasiewicz switch (_ds->bDescriptorType) {
204000a2430fSAndrzej Pietrasiewicz case USB_DT_DEVICE:
204100a2430fSAndrzej Pietrasiewicz case USB_DT_CONFIG:
204200a2430fSAndrzej Pietrasiewicz case USB_DT_STRING:
204300a2430fSAndrzej Pietrasiewicz case USB_DT_DEVICE_QUALIFIER:
204400a2430fSAndrzej Pietrasiewicz /* function can't have any of those */
204500a2430fSAndrzej Pietrasiewicz pr_vdebug("descriptor reserved for gadget: %d\n",
204600a2430fSAndrzej Pietrasiewicz _ds->bDescriptorType);
204700a2430fSAndrzej Pietrasiewicz return -EINVAL;
204800a2430fSAndrzej Pietrasiewicz
204900a2430fSAndrzej Pietrasiewicz case USB_DT_INTERFACE: {
205000a2430fSAndrzej Pietrasiewicz struct usb_interface_descriptor *ds = (void *)_ds;
205100a2430fSAndrzej Pietrasiewicz pr_vdebug("interface descriptor\n");
205200a2430fSAndrzej Pietrasiewicz if (length != sizeof *ds)
205300a2430fSAndrzej Pietrasiewicz goto inv_length;
205400a2430fSAndrzej Pietrasiewicz
205500a2430fSAndrzej Pietrasiewicz __entity(INTERFACE, ds->bInterfaceNumber);
205600a2430fSAndrzej Pietrasiewicz if (ds->iInterface)
205700a2430fSAndrzej Pietrasiewicz __entity(STRING, ds->iInterface);
20587f7c548cSVincent Pelletier *current_class = ds->bInterfaceClass;
205900a2430fSAndrzej Pietrasiewicz }
206000a2430fSAndrzej Pietrasiewicz break;
206100a2430fSAndrzej Pietrasiewicz
206200a2430fSAndrzej Pietrasiewicz case USB_DT_ENDPOINT: {
206300a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor *ds = (void *)_ds;
206400a2430fSAndrzej Pietrasiewicz pr_vdebug("endpoint descriptor\n");
206500a2430fSAndrzej Pietrasiewicz if (length != USB_DT_ENDPOINT_SIZE &&
206600a2430fSAndrzej Pietrasiewicz length != USB_DT_ENDPOINT_AUDIO_SIZE)
206700a2430fSAndrzej Pietrasiewicz goto inv_length;
206800a2430fSAndrzej Pietrasiewicz __entity(ENDPOINT, ds->bEndpointAddress);
206900a2430fSAndrzej Pietrasiewicz }
207000a2430fSAndrzej Pietrasiewicz break;
207100a2430fSAndrzej Pietrasiewicz
20727f7c548cSVincent Pelletier case USB_TYPE_CLASS | 0x01:
20737f7c548cSVincent Pelletier if (*current_class == USB_INTERFACE_CLASS_HID) {
207400a2430fSAndrzej Pietrasiewicz pr_vdebug("hid descriptor\n");
207500a2430fSAndrzej Pietrasiewicz if (length != sizeof(struct hid_descriptor))
207600a2430fSAndrzej Pietrasiewicz goto inv_length;
207700a2430fSAndrzej Pietrasiewicz break;
20787f7c548cSVincent Pelletier } else if (*current_class == USB_INTERFACE_CLASS_CCID) {
20797f7c548cSVincent Pelletier pr_vdebug("ccid descriptor\n");
20807f7c548cSVincent Pelletier if (length != sizeof(struct ccid_descriptor))
20817f7c548cSVincent Pelletier goto inv_length;
20827f7c548cSVincent Pelletier break;
20837f7c548cSVincent Pelletier } else {
20847f7c548cSVincent Pelletier pr_vdebug("unknown descriptor: %d for class %d\n",
20857f7c548cSVincent Pelletier _ds->bDescriptorType, *current_class);
20867f7c548cSVincent Pelletier return -EINVAL;
20877f7c548cSVincent Pelletier }
208800a2430fSAndrzej Pietrasiewicz
208900a2430fSAndrzej Pietrasiewicz case USB_DT_OTG:
209000a2430fSAndrzej Pietrasiewicz if (length != sizeof(struct usb_otg_descriptor))
209100a2430fSAndrzej Pietrasiewicz goto inv_length;
209200a2430fSAndrzej Pietrasiewicz break;
209300a2430fSAndrzej Pietrasiewicz
209400a2430fSAndrzej Pietrasiewicz case USB_DT_INTERFACE_ASSOCIATION: {
209500a2430fSAndrzej Pietrasiewicz struct usb_interface_assoc_descriptor *ds = (void *)_ds;
209600a2430fSAndrzej Pietrasiewicz pr_vdebug("interface association descriptor\n");
209700a2430fSAndrzej Pietrasiewicz if (length != sizeof *ds)
209800a2430fSAndrzej Pietrasiewicz goto inv_length;
209900a2430fSAndrzej Pietrasiewicz if (ds->iFunction)
210000a2430fSAndrzej Pietrasiewicz __entity(STRING, ds->iFunction);
210100a2430fSAndrzej Pietrasiewicz }
210200a2430fSAndrzej Pietrasiewicz break;
210300a2430fSAndrzej Pietrasiewicz
210400a2430fSAndrzej Pietrasiewicz case USB_DT_SS_ENDPOINT_COMP:
210500a2430fSAndrzej Pietrasiewicz pr_vdebug("EP SS companion descriptor\n");
210600a2430fSAndrzej Pietrasiewicz if (length != sizeof(struct usb_ss_ep_comp_descriptor))
210700a2430fSAndrzej Pietrasiewicz goto inv_length;
210800a2430fSAndrzej Pietrasiewicz break;
210900a2430fSAndrzej Pietrasiewicz
211000a2430fSAndrzej Pietrasiewicz case USB_DT_OTHER_SPEED_CONFIG:
211100a2430fSAndrzej Pietrasiewicz case USB_DT_INTERFACE_POWER:
211200a2430fSAndrzej Pietrasiewicz case USB_DT_DEBUG:
211300a2430fSAndrzej Pietrasiewicz case USB_DT_SECURITY:
211400a2430fSAndrzej Pietrasiewicz case USB_DT_CS_RADIO_CONTROL:
211500a2430fSAndrzej Pietrasiewicz /* TODO */
211600a2430fSAndrzej Pietrasiewicz pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
211700a2430fSAndrzej Pietrasiewicz return -EINVAL;
211800a2430fSAndrzej Pietrasiewicz
211900a2430fSAndrzej Pietrasiewicz default:
212000a2430fSAndrzej Pietrasiewicz /* We should never be here */
212100a2430fSAndrzej Pietrasiewicz pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
212200a2430fSAndrzej Pietrasiewicz return -EINVAL;
212300a2430fSAndrzej Pietrasiewicz
212400a2430fSAndrzej Pietrasiewicz inv_length:
212500a2430fSAndrzej Pietrasiewicz pr_vdebug("invalid length: %d (descriptor %d)\n",
212600a2430fSAndrzej Pietrasiewicz _ds->bLength, _ds->bDescriptorType);
212700a2430fSAndrzej Pietrasiewicz return -EINVAL;
212800a2430fSAndrzej Pietrasiewicz }
212900a2430fSAndrzej Pietrasiewicz
213000a2430fSAndrzej Pietrasiewicz #undef __entity
213100a2430fSAndrzej Pietrasiewicz #undef __entity_check_DESCRIPTOR
213200a2430fSAndrzej Pietrasiewicz #undef __entity_check_INTERFACE
213300a2430fSAndrzej Pietrasiewicz #undef __entity_check_STRING
213400a2430fSAndrzej Pietrasiewicz #undef __entity_check_ENDPOINT
213500a2430fSAndrzej Pietrasiewicz
213600a2430fSAndrzej Pietrasiewicz return length;
213700a2430fSAndrzej Pietrasiewicz }
213800a2430fSAndrzej Pietrasiewicz
ffs_do_descs(unsigned count,char * data,unsigned len,ffs_entity_callback entity,void * priv)213900a2430fSAndrzej Pietrasiewicz static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
214000a2430fSAndrzej Pietrasiewicz ffs_entity_callback entity, void *priv)
214100a2430fSAndrzej Pietrasiewicz {
214200a2430fSAndrzej Pietrasiewicz const unsigned _len = len;
214300a2430fSAndrzej Pietrasiewicz unsigned long num = 0;
21447f7c548cSVincent Pelletier int current_class = -1;
214500a2430fSAndrzej Pietrasiewicz
214600a2430fSAndrzej Pietrasiewicz for (;;) {
214700a2430fSAndrzej Pietrasiewicz int ret;
214800a2430fSAndrzej Pietrasiewicz
214900a2430fSAndrzej Pietrasiewicz if (num == count)
215000a2430fSAndrzej Pietrasiewicz data = NULL;
215100a2430fSAndrzej Pietrasiewicz
215200a2430fSAndrzej Pietrasiewicz /* Record "descriptor" entity */
215300a2430fSAndrzej Pietrasiewicz ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
21548704fd73SGreg Kroah-Hartman if (ret < 0) {
215500a2430fSAndrzej Pietrasiewicz pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
215600a2430fSAndrzej Pietrasiewicz num, ret);
215700a2430fSAndrzej Pietrasiewicz return ret;
215800a2430fSAndrzej Pietrasiewicz }
215900a2430fSAndrzej Pietrasiewicz
216000a2430fSAndrzej Pietrasiewicz if (!data)
216100a2430fSAndrzej Pietrasiewicz return _len - len;
216200a2430fSAndrzej Pietrasiewicz
21637f7c548cSVincent Pelletier ret = ffs_do_single_desc(data, len, entity, priv,
21647f7c548cSVincent Pelletier ¤t_class);
21658704fd73SGreg Kroah-Hartman if (ret < 0) {
216600a2430fSAndrzej Pietrasiewicz pr_debug("%s returns %d\n", __func__, ret);
216700a2430fSAndrzej Pietrasiewicz return ret;
216800a2430fSAndrzej Pietrasiewicz }
216900a2430fSAndrzej Pietrasiewicz
217000a2430fSAndrzej Pietrasiewicz len -= ret;
217100a2430fSAndrzej Pietrasiewicz data += ret;
217200a2430fSAndrzej Pietrasiewicz ++num;
217300a2430fSAndrzej Pietrasiewicz }
217400a2430fSAndrzej Pietrasiewicz }
217500a2430fSAndrzej Pietrasiewicz
__ffs_data_do_entity(enum ffs_entity_type type,u8 * valuep,struct usb_descriptor_header * desc,void * priv)217600a2430fSAndrzej Pietrasiewicz static int __ffs_data_do_entity(enum ffs_entity_type type,
217700a2430fSAndrzej Pietrasiewicz u8 *valuep, struct usb_descriptor_header *desc,
217800a2430fSAndrzej Pietrasiewicz void *priv)
217900a2430fSAndrzej Pietrasiewicz {
21806d5c1c77SRobert Baldyga struct ffs_desc_helper *helper = priv;
21816d5c1c77SRobert Baldyga struct usb_endpoint_descriptor *d;
218200a2430fSAndrzej Pietrasiewicz
218300a2430fSAndrzej Pietrasiewicz switch (type) {
218400a2430fSAndrzej Pietrasiewicz case FFS_DESCRIPTOR:
218500a2430fSAndrzej Pietrasiewicz break;
218600a2430fSAndrzej Pietrasiewicz
218700a2430fSAndrzej Pietrasiewicz case FFS_INTERFACE:
218800a2430fSAndrzej Pietrasiewicz /*
218900a2430fSAndrzej Pietrasiewicz * Interfaces are indexed from zero so if we
219000a2430fSAndrzej Pietrasiewicz * encountered interface "n" then there are at least
219100a2430fSAndrzej Pietrasiewicz * "n+1" interfaces.
219200a2430fSAndrzej Pietrasiewicz */
21936d5c1c77SRobert Baldyga if (*valuep >= helper->interfaces_count)
21946d5c1c77SRobert Baldyga helper->interfaces_count = *valuep + 1;
219500a2430fSAndrzej Pietrasiewicz break;
219600a2430fSAndrzej Pietrasiewicz
219700a2430fSAndrzej Pietrasiewicz case FFS_STRING:
219800a2430fSAndrzej Pietrasiewicz /*
219996a420d2SVincent Pelletier * Strings are indexed from 1 (0 is reserved
220096a420d2SVincent Pelletier * for languages list)
220100a2430fSAndrzej Pietrasiewicz */
22026d5c1c77SRobert Baldyga if (*valuep > helper->ffs->strings_count)
22036d5c1c77SRobert Baldyga helper->ffs->strings_count = *valuep;
220400a2430fSAndrzej Pietrasiewicz break;
220500a2430fSAndrzej Pietrasiewicz
220600a2430fSAndrzej Pietrasiewicz case FFS_ENDPOINT:
22076d5c1c77SRobert Baldyga d = (void *)desc;
22086d5c1c77SRobert Baldyga helper->eps_count++;
220941dc9ac1SVincent Pelletier if (helper->eps_count >= FFS_MAX_EPS_COUNT)
22106d5c1c77SRobert Baldyga return -EINVAL;
22116d5c1c77SRobert Baldyga /* Check if descriptors for any speed were already parsed */
22126d5c1c77SRobert Baldyga if (!helper->ffs->eps_count && !helper->ffs->interfaces_count)
22136d5c1c77SRobert Baldyga helper->ffs->eps_addrmap[helper->eps_count] =
22146d5c1c77SRobert Baldyga d->bEndpointAddress;
22156d5c1c77SRobert Baldyga else if (helper->ffs->eps_addrmap[helper->eps_count] !=
22166d5c1c77SRobert Baldyga d->bEndpointAddress)
22176d5c1c77SRobert Baldyga return -EINVAL;
221800a2430fSAndrzej Pietrasiewicz break;
221900a2430fSAndrzej Pietrasiewicz }
222000a2430fSAndrzej Pietrasiewicz
222100a2430fSAndrzej Pietrasiewicz return 0;
222200a2430fSAndrzej Pietrasiewicz }
222300a2430fSAndrzej Pietrasiewicz
__ffs_do_os_desc_header(enum ffs_os_desc_type * next_type,struct usb_os_desc_header * desc)222400a2430fSAndrzej Pietrasiewicz static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
222500a2430fSAndrzej Pietrasiewicz struct usb_os_desc_header *desc)
222600a2430fSAndrzej Pietrasiewicz {
222700a2430fSAndrzej Pietrasiewicz u16 bcd_version = le16_to_cpu(desc->bcdVersion);
222800a2430fSAndrzej Pietrasiewicz u16 w_index = le16_to_cpu(desc->wIndex);
222900a2430fSAndrzej Pietrasiewicz
2230180bb831SYuta Hayama if (bcd_version == 0x1) {
2231180bb831SYuta Hayama pr_warn("bcdVersion must be 0x0100, stored in Little Endian order. "
2232180bb831SYuta Hayama "Userspace driver should be fixed, accepting 0x0001 for compatibility.\n");
2233180bb831SYuta Hayama } else if (bcd_version != 0x100) {
2234180bb831SYuta Hayama pr_vdebug("unsupported os descriptors version: 0x%x\n",
223500a2430fSAndrzej Pietrasiewicz bcd_version);
223600a2430fSAndrzej Pietrasiewicz return -EINVAL;
223700a2430fSAndrzej Pietrasiewicz }
223800a2430fSAndrzej Pietrasiewicz switch (w_index) {
223900a2430fSAndrzej Pietrasiewicz case 0x4:
224000a2430fSAndrzej Pietrasiewicz *next_type = FFS_OS_DESC_EXT_COMPAT;
224100a2430fSAndrzej Pietrasiewicz break;
224200a2430fSAndrzej Pietrasiewicz case 0x5:
224300a2430fSAndrzej Pietrasiewicz *next_type = FFS_OS_DESC_EXT_PROP;
224400a2430fSAndrzej Pietrasiewicz break;
224500a2430fSAndrzej Pietrasiewicz default:
224600a2430fSAndrzej Pietrasiewicz pr_vdebug("unsupported os descriptor type: %d", w_index);
224700a2430fSAndrzej Pietrasiewicz return -EINVAL;
224800a2430fSAndrzej Pietrasiewicz }
224900a2430fSAndrzej Pietrasiewicz
225000a2430fSAndrzej Pietrasiewicz return sizeof(*desc);
225100a2430fSAndrzej Pietrasiewicz }
225200a2430fSAndrzej Pietrasiewicz
225300a2430fSAndrzej Pietrasiewicz /*
225400a2430fSAndrzej Pietrasiewicz * Process all extended compatibility/extended property descriptors
225500a2430fSAndrzej Pietrasiewicz * of a feature descriptor
225600a2430fSAndrzej Pietrasiewicz */
ffs_do_single_os_desc(char * data,unsigned len,enum ffs_os_desc_type type,u16 feature_count,ffs_os_desc_callback entity,void * priv,struct usb_os_desc_header * h)225700a2430fSAndrzej Pietrasiewicz static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
225800a2430fSAndrzej Pietrasiewicz enum ffs_os_desc_type type,
225900a2430fSAndrzej Pietrasiewicz u16 feature_count,
226000a2430fSAndrzej Pietrasiewicz ffs_os_desc_callback entity,
226100a2430fSAndrzej Pietrasiewicz void *priv,
226200a2430fSAndrzej Pietrasiewicz struct usb_os_desc_header *h)
226300a2430fSAndrzej Pietrasiewicz {
226400a2430fSAndrzej Pietrasiewicz int ret;
226500a2430fSAndrzej Pietrasiewicz const unsigned _len = len;
226600a2430fSAndrzej Pietrasiewicz
226700a2430fSAndrzej Pietrasiewicz /* loop over all ext compat/ext prop descriptors */
226800a2430fSAndrzej Pietrasiewicz while (feature_count--) {
226900a2430fSAndrzej Pietrasiewicz ret = entity(type, h, data, len, priv);
22708704fd73SGreg Kroah-Hartman if (ret < 0) {
227100a2430fSAndrzej Pietrasiewicz pr_debug("bad OS descriptor, type: %d\n", type);
227200a2430fSAndrzej Pietrasiewicz return ret;
227300a2430fSAndrzej Pietrasiewicz }
227400a2430fSAndrzej Pietrasiewicz data += ret;
227500a2430fSAndrzej Pietrasiewicz len -= ret;
227600a2430fSAndrzej Pietrasiewicz }
227700a2430fSAndrzej Pietrasiewicz return _len - len;
227800a2430fSAndrzej Pietrasiewicz }
227900a2430fSAndrzej Pietrasiewicz
228000a2430fSAndrzej Pietrasiewicz /* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
ffs_do_os_descs(unsigned count,char * data,unsigned len,ffs_os_desc_callback entity,void * priv)228100a2430fSAndrzej Pietrasiewicz static int __must_check ffs_do_os_descs(unsigned count,
228200a2430fSAndrzej Pietrasiewicz char *data, unsigned len,
228300a2430fSAndrzej Pietrasiewicz ffs_os_desc_callback entity, void *priv)
228400a2430fSAndrzej Pietrasiewicz {
228500a2430fSAndrzej Pietrasiewicz const unsigned _len = len;
228600a2430fSAndrzej Pietrasiewicz unsigned long num = 0;
228700a2430fSAndrzej Pietrasiewicz
228800a2430fSAndrzej Pietrasiewicz for (num = 0; num < count; ++num) {
228900a2430fSAndrzej Pietrasiewicz int ret;
229000a2430fSAndrzej Pietrasiewicz enum ffs_os_desc_type type;
229100a2430fSAndrzej Pietrasiewicz u16 feature_count;
229200a2430fSAndrzej Pietrasiewicz struct usb_os_desc_header *desc = (void *)data;
229300a2430fSAndrzej Pietrasiewicz
229400a2430fSAndrzej Pietrasiewicz if (len < sizeof(*desc))
229500a2430fSAndrzej Pietrasiewicz return -EINVAL;
229600a2430fSAndrzej Pietrasiewicz
229700a2430fSAndrzej Pietrasiewicz /*
229800a2430fSAndrzej Pietrasiewicz * Record "descriptor" entity.
229900a2430fSAndrzej Pietrasiewicz * Process dwLength, bcdVersion, wIndex, get b/wCount.
230000a2430fSAndrzej Pietrasiewicz * Move the data pointer to the beginning of extended
230100a2430fSAndrzej Pietrasiewicz * compatibilities proper or extended properties proper
230200a2430fSAndrzej Pietrasiewicz * portions of the data
230300a2430fSAndrzej Pietrasiewicz */
230400a2430fSAndrzej Pietrasiewicz if (le32_to_cpu(desc->dwLength) > len)
230500a2430fSAndrzej Pietrasiewicz return -EINVAL;
230600a2430fSAndrzej Pietrasiewicz
230700a2430fSAndrzej Pietrasiewicz ret = __ffs_do_os_desc_header(&type, desc);
23088704fd73SGreg Kroah-Hartman if (ret < 0) {
230900a2430fSAndrzej Pietrasiewicz pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
231000a2430fSAndrzej Pietrasiewicz num, ret);
231100a2430fSAndrzej Pietrasiewicz return ret;
231200a2430fSAndrzej Pietrasiewicz }
231300a2430fSAndrzej Pietrasiewicz /*
231400a2430fSAndrzej Pietrasiewicz * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
231500a2430fSAndrzej Pietrasiewicz */
231600a2430fSAndrzej Pietrasiewicz feature_count = le16_to_cpu(desc->wCount);
231700a2430fSAndrzej Pietrasiewicz if (type == FFS_OS_DESC_EXT_COMPAT &&
231800a2430fSAndrzej Pietrasiewicz (feature_count > 255 || desc->Reserved))
231900a2430fSAndrzej Pietrasiewicz return -EINVAL;
232000a2430fSAndrzej Pietrasiewicz len -= ret;
232100a2430fSAndrzej Pietrasiewicz data += ret;
232200a2430fSAndrzej Pietrasiewicz
232300a2430fSAndrzej Pietrasiewicz /*
232400a2430fSAndrzej Pietrasiewicz * Process all function/property descriptors
232500a2430fSAndrzej Pietrasiewicz * of this Feature Descriptor
232600a2430fSAndrzej Pietrasiewicz */
232700a2430fSAndrzej Pietrasiewicz ret = ffs_do_single_os_desc(data, len, type,
232800a2430fSAndrzej Pietrasiewicz feature_count, entity, priv, desc);
23298704fd73SGreg Kroah-Hartman if (ret < 0) {
233000a2430fSAndrzej Pietrasiewicz pr_debug("%s returns %d\n", __func__, ret);
233100a2430fSAndrzej Pietrasiewicz return ret;
233200a2430fSAndrzej Pietrasiewicz }
233300a2430fSAndrzej Pietrasiewicz
233400a2430fSAndrzej Pietrasiewicz len -= ret;
233500a2430fSAndrzej Pietrasiewicz data += ret;
233600a2430fSAndrzej Pietrasiewicz }
233700a2430fSAndrzej Pietrasiewicz return _len - len;
233800a2430fSAndrzej Pietrasiewicz }
233900a2430fSAndrzej Pietrasiewicz
23408f9a0e10SLee Jones /*
234100a2430fSAndrzej Pietrasiewicz * Validate contents of the buffer from userspace related to OS descriptors.
234200a2430fSAndrzej Pietrasiewicz */
__ffs_data_do_os_desc(enum ffs_os_desc_type type,struct usb_os_desc_header * h,void * data,unsigned len,void * priv)234300a2430fSAndrzej Pietrasiewicz static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
234400a2430fSAndrzej Pietrasiewicz struct usb_os_desc_header *h, void *data,
234500a2430fSAndrzej Pietrasiewicz unsigned len, void *priv)
234600a2430fSAndrzej Pietrasiewicz {
234700a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = priv;
234800a2430fSAndrzej Pietrasiewicz u8 length;
234900a2430fSAndrzej Pietrasiewicz
235000a2430fSAndrzej Pietrasiewicz switch (type) {
235100a2430fSAndrzej Pietrasiewicz case FFS_OS_DESC_EXT_COMPAT: {
235200a2430fSAndrzej Pietrasiewicz struct usb_ext_compat_desc *d = data;
235300a2430fSAndrzej Pietrasiewicz int i;
235400a2430fSAndrzej Pietrasiewicz
235500a2430fSAndrzej Pietrasiewicz if (len < sizeof(*d) ||
2356a3acc696SJohn Keeping d->bFirstInterfaceNumber >= ffs->interfaces_count)
235700a2430fSAndrzej Pietrasiewicz return -EINVAL;
2358a3acc696SJohn Keeping if (d->Reserved1 != 1) {
2359a3acc696SJohn Keeping /*
2360a3acc696SJohn Keeping * According to the spec, Reserved1 must be set to 1
2361a3acc696SJohn Keeping * but older kernels incorrectly rejected non-zero
2362a3acc696SJohn Keeping * values. We fix it here to avoid returning EINVAL
2363a3acc696SJohn Keeping * in response to values we used to accept.
2364a3acc696SJohn Keeping */
2365a3acc696SJohn Keeping pr_debug("usb_ext_compat_desc::Reserved1 forced to 1\n");
2366a3acc696SJohn Keeping d->Reserved1 = 1;
2367a3acc696SJohn Keeping }
236800a2430fSAndrzej Pietrasiewicz for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
236900a2430fSAndrzej Pietrasiewicz if (d->Reserved2[i])
237000a2430fSAndrzej Pietrasiewicz return -EINVAL;
237100a2430fSAndrzej Pietrasiewicz
237200a2430fSAndrzej Pietrasiewicz length = sizeof(struct usb_ext_compat_desc);
237300a2430fSAndrzej Pietrasiewicz }
237400a2430fSAndrzej Pietrasiewicz break;
237500a2430fSAndrzej Pietrasiewicz case FFS_OS_DESC_EXT_PROP: {
237600a2430fSAndrzej Pietrasiewicz struct usb_ext_prop_desc *d = data;
237700a2430fSAndrzej Pietrasiewicz u32 type, pdl;
237800a2430fSAndrzej Pietrasiewicz u16 pnl;
237900a2430fSAndrzej Pietrasiewicz
238000a2430fSAndrzej Pietrasiewicz if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
238100a2430fSAndrzej Pietrasiewicz return -EINVAL;
238200a2430fSAndrzej Pietrasiewicz length = le32_to_cpu(d->dwSize);
238383e526f2SVincent Pelletier if (len < length)
238483e526f2SVincent Pelletier return -EINVAL;
238500a2430fSAndrzej Pietrasiewicz type = le32_to_cpu(d->dwPropertyDataType);
238600a2430fSAndrzej Pietrasiewicz if (type < USB_EXT_PROP_UNICODE ||
238700a2430fSAndrzej Pietrasiewicz type > USB_EXT_PROP_UNICODE_MULTI) {
238800a2430fSAndrzej Pietrasiewicz pr_vdebug("unsupported os descriptor property type: %d",
238900a2430fSAndrzej Pietrasiewicz type);
239000a2430fSAndrzej Pietrasiewicz return -EINVAL;
239100a2430fSAndrzej Pietrasiewicz }
239200a2430fSAndrzej Pietrasiewicz pnl = le16_to_cpu(d->wPropertyNameLength);
239383e526f2SVincent Pelletier if (length < 14 + pnl) {
239483e526f2SVincent Pelletier pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n",
239583e526f2SVincent Pelletier length, pnl, type);
239683e526f2SVincent Pelletier return -EINVAL;
239783e526f2SVincent Pelletier }
2398c40619bbSVincent Pelletier pdl = le32_to_cpu(*(__le32 *)((u8 *)data + 10 + pnl));
239900a2430fSAndrzej Pietrasiewicz if (length != 14 + pnl + pdl) {
240000a2430fSAndrzej Pietrasiewicz pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
240100a2430fSAndrzej Pietrasiewicz length, pnl, pdl, type);
240200a2430fSAndrzej Pietrasiewicz return -EINVAL;
240300a2430fSAndrzej Pietrasiewicz }
240400a2430fSAndrzej Pietrasiewicz ++ffs->ms_os_descs_ext_prop_count;
240500a2430fSAndrzej Pietrasiewicz /* property name reported to the host as "WCHAR"s */
240600a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
240700a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_data_len += pdl;
240800a2430fSAndrzej Pietrasiewicz }
240900a2430fSAndrzej Pietrasiewicz break;
241000a2430fSAndrzej Pietrasiewicz default:
241100a2430fSAndrzej Pietrasiewicz pr_vdebug("unknown descriptor: %d\n", type);
241200a2430fSAndrzej Pietrasiewicz return -EINVAL;
241300a2430fSAndrzej Pietrasiewicz }
241400a2430fSAndrzej Pietrasiewicz return length;
241500a2430fSAndrzej Pietrasiewicz }
241600a2430fSAndrzej Pietrasiewicz
__ffs_data_got_descs(struct ffs_data * ffs,char * const _data,size_t len)241700a2430fSAndrzej Pietrasiewicz static int __ffs_data_got_descs(struct ffs_data *ffs,
241800a2430fSAndrzej Pietrasiewicz char *const _data, size_t len)
241900a2430fSAndrzej Pietrasiewicz {
242000a2430fSAndrzej Pietrasiewicz char *data = _data, *raw_descs;
242100a2430fSAndrzej Pietrasiewicz unsigned os_descs_count = 0, counts[3], flags;
242200a2430fSAndrzej Pietrasiewicz int ret = -EINVAL, i;
24236d5c1c77SRobert Baldyga struct ffs_desc_helper helper;
242400a2430fSAndrzej Pietrasiewicz
242500a2430fSAndrzej Pietrasiewicz if (get_unaligned_le32(data + 4) != len)
242600a2430fSAndrzej Pietrasiewicz goto error;
242700a2430fSAndrzej Pietrasiewicz
242800a2430fSAndrzej Pietrasiewicz switch (get_unaligned_le32(data)) {
242900a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_DESCRIPTORS_MAGIC:
243000a2430fSAndrzej Pietrasiewicz flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
243100a2430fSAndrzej Pietrasiewicz data += 8;
243200a2430fSAndrzej Pietrasiewicz len -= 8;
243300a2430fSAndrzej Pietrasiewicz break;
243400a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
243500a2430fSAndrzej Pietrasiewicz flags = get_unaligned_le32(data + 8);
24361b0bf88fSRobert Baldyga ffs->user_flags = flags;
243700a2430fSAndrzej Pietrasiewicz if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
243800a2430fSAndrzej Pietrasiewicz FUNCTIONFS_HAS_HS_DESC |
243900a2430fSAndrzej Pietrasiewicz FUNCTIONFS_HAS_SS_DESC |
24401b0bf88fSRobert Baldyga FUNCTIONFS_HAS_MS_OS_DESC |
24415e33f6fdSRobert Baldyga FUNCTIONFS_VIRTUAL_ADDR |
244254dfce6dSFelix Hädicke FUNCTIONFS_EVENTFD |
24434368c28aSFelix Hädicke FUNCTIONFS_ALL_CTRL_RECIP |
24444368c28aSFelix Hädicke FUNCTIONFS_CONFIG0_SETUP)) {
244500a2430fSAndrzej Pietrasiewicz ret = -ENOSYS;
244600a2430fSAndrzej Pietrasiewicz goto error;
244700a2430fSAndrzej Pietrasiewicz }
244800a2430fSAndrzej Pietrasiewicz data += 12;
244900a2430fSAndrzej Pietrasiewicz len -= 12;
245000a2430fSAndrzej Pietrasiewicz break;
245100a2430fSAndrzej Pietrasiewicz default:
245200a2430fSAndrzej Pietrasiewicz goto error;
245300a2430fSAndrzej Pietrasiewicz }
245400a2430fSAndrzej Pietrasiewicz
24555e33f6fdSRobert Baldyga if (flags & FUNCTIONFS_EVENTFD) {
24565e33f6fdSRobert Baldyga if (len < 4)
24575e33f6fdSRobert Baldyga goto error;
24585e33f6fdSRobert Baldyga ffs->ffs_eventfd =
24595e33f6fdSRobert Baldyga eventfd_ctx_fdget((int)get_unaligned_le32(data));
24605e33f6fdSRobert Baldyga if (IS_ERR(ffs->ffs_eventfd)) {
24615e33f6fdSRobert Baldyga ret = PTR_ERR(ffs->ffs_eventfd);
24625e33f6fdSRobert Baldyga ffs->ffs_eventfd = NULL;
24635e33f6fdSRobert Baldyga goto error;
24645e33f6fdSRobert Baldyga }
24655e33f6fdSRobert Baldyga data += 4;
24665e33f6fdSRobert Baldyga len -= 4;
24675e33f6fdSRobert Baldyga }
24685e33f6fdSRobert Baldyga
246900a2430fSAndrzej Pietrasiewicz /* Read fs_count, hs_count and ss_count (if present) */
247000a2430fSAndrzej Pietrasiewicz for (i = 0; i < 3; ++i) {
247100a2430fSAndrzej Pietrasiewicz if (!(flags & (1 << i))) {
247200a2430fSAndrzej Pietrasiewicz counts[i] = 0;
247300a2430fSAndrzej Pietrasiewicz } else if (len < 4) {
247400a2430fSAndrzej Pietrasiewicz goto error;
247500a2430fSAndrzej Pietrasiewicz } else {
247600a2430fSAndrzej Pietrasiewicz counts[i] = get_unaligned_le32(data);
247700a2430fSAndrzej Pietrasiewicz data += 4;
247800a2430fSAndrzej Pietrasiewicz len -= 4;
247900a2430fSAndrzej Pietrasiewicz }
248000a2430fSAndrzej Pietrasiewicz }
248100a2430fSAndrzej Pietrasiewicz if (flags & (1 << i)) {
248283e526f2SVincent Pelletier if (len < 4) {
248383e526f2SVincent Pelletier goto error;
248483e526f2SVincent Pelletier }
248500a2430fSAndrzej Pietrasiewicz os_descs_count = get_unaligned_le32(data);
248600a2430fSAndrzej Pietrasiewicz data += 4;
248700a2430fSAndrzej Pietrasiewicz len -= 4;
2488a54177d2SJason Yan }
248900a2430fSAndrzej Pietrasiewicz
249000a2430fSAndrzej Pietrasiewicz /* Read descriptors */
249100a2430fSAndrzej Pietrasiewicz raw_descs = data;
24926d5c1c77SRobert Baldyga helper.ffs = ffs;
249300a2430fSAndrzej Pietrasiewicz for (i = 0; i < 3; ++i) {
249400a2430fSAndrzej Pietrasiewicz if (!counts[i])
249500a2430fSAndrzej Pietrasiewicz continue;
24966d5c1c77SRobert Baldyga helper.interfaces_count = 0;
24976d5c1c77SRobert Baldyga helper.eps_count = 0;
249800a2430fSAndrzej Pietrasiewicz ret = ffs_do_descs(counts[i], data, len,
24996d5c1c77SRobert Baldyga __ffs_data_do_entity, &helper);
250000a2430fSAndrzej Pietrasiewicz if (ret < 0)
250100a2430fSAndrzej Pietrasiewicz goto error;
25026d5c1c77SRobert Baldyga if (!ffs->eps_count && !ffs->interfaces_count) {
25036d5c1c77SRobert Baldyga ffs->eps_count = helper.eps_count;
25046d5c1c77SRobert Baldyga ffs->interfaces_count = helper.interfaces_count;
25056d5c1c77SRobert Baldyga } else {
25066d5c1c77SRobert Baldyga if (ffs->eps_count != helper.eps_count) {
25076d5c1c77SRobert Baldyga ret = -EINVAL;
25086d5c1c77SRobert Baldyga goto error;
25096d5c1c77SRobert Baldyga }
25106d5c1c77SRobert Baldyga if (ffs->interfaces_count != helper.interfaces_count) {
25116d5c1c77SRobert Baldyga ret = -EINVAL;
25126d5c1c77SRobert Baldyga goto error;
25136d5c1c77SRobert Baldyga }
25146d5c1c77SRobert Baldyga }
251500a2430fSAndrzej Pietrasiewicz data += ret;
251600a2430fSAndrzej Pietrasiewicz len -= ret;
251700a2430fSAndrzej Pietrasiewicz }
251800a2430fSAndrzej Pietrasiewicz if (os_descs_count) {
251900a2430fSAndrzej Pietrasiewicz ret = ffs_do_os_descs(os_descs_count, data, len,
252000a2430fSAndrzej Pietrasiewicz __ffs_data_do_os_desc, ffs);
252100a2430fSAndrzej Pietrasiewicz if (ret < 0)
252200a2430fSAndrzej Pietrasiewicz goto error;
252300a2430fSAndrzej Pietrasiewicz data += ret;
252400a2430fSAndrzej Pietrasiewicz len -= ret;
252500a2430fSAndrzej Pietrasiewicz }
252600a2430fSAndrzej Pietrasiewicz
252700a2430fSAndrzej Pietrasiewicz if (raw_descs == data || len) {
252800a2430fSAndrzej Pietrasiewicz ret = -EINVAL;
252900a2430fSAndrzej Pietrasiewicz goto error;
253000a2430fSAndrzej Pietrasiewicz }
253100a2430fSAndrzej Pietrasiewicz
253200a2430fSAndrzej Pietrasiewicz ffs->raw_descs_data = _data;
253300a2430fSAndrzej Pietrasiewicz ffs->raw_descs = raw_descs;
253400a2430fSAndrzej Pietrasiewicz ffs->raw_descs_length = data - raw_descs;
253500a2430fSAndrzej Pietrasiewicz ffs->fs_descs_count = counts[0];
253600a2430fSAndrzej Pietrasiewicz ffs->hs_descs_count = counts[1];
253700a2430fSAndrzej Pietrasiewicz ffs->ss_descs_count = counts[2];
253800a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_count = os_descs_count;
253900a2430fSAndrzej Pietrasiewicz
254000a2430fSAndrzej Pietrasiewicz return 0;
254100a2430fSAndrzej Pietrasiewicz
254200a2430fSAndrzej Pietrasiewicz error:
254300a2430fSAndrzej Pietrasiewicz kfree(_data);
254400a2430fSAndrzej Pietrasiewicz return ret;
254500a2430fSAndrzej Pietrasiewicz }
254600a2430fSAndrzej Pietrasiewicz
__ffs_data_got_strings(struct ffs_data * ffs,char * const _data,size_t len)254700a2430fSAndrzej Pietrasiewicz static int __ffs_data_got_strings(struct ffs_data *ffs,
254800a2430fSAndrzej Pietrasiewicz char *const _data, size_t len)
254900a2430fSAndrzej Pietrasiewicz {
255000a2430fSAndrzej Pietrasiewicz u32 str_count, needed_count, lang_count;
255100a2430fSAndrzej Pietrasiewicz struct usb_gadget_strings **stringtabs, *t;
255200a2430fSAndrzej Pietrasiewicz const char *data = _data;
2553872ce511SMichal Nazarewicz struct usb_string *s;
255400a2430fSAndrzej Pietrasiewicz
25558704fd73SGreg Kroah-Hartman if (len < 16 ||
255683e526f2SVincent Pelletier get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
25578704fd73SGreg Kroah-Hartman get_unaligned_le32(data + 4) != len)
255800a2430fSAndrzej Pietrasiewicz goto error;
255900a2430fSAndrzej Pietrasiewicz str_count = get_unaligned_le32(data + 8);
256000a2430fSAndrzej Pietrasiewicz lang_count = get_unaligned_le32(data + 12);
256100a2430fSAndrzej Pietrasiewicz
256200a2430fSAndrzej Pietrasiewicz /* if one is zero the other must be zero */
25638704fd73SGreg Kroah-Hartman if (!str_count != !lang_count)
256400a2430fSAndrzej Pietrasiewicz goto error;
256500a2430fSAndrzej Pietrasiewicz
256600a2430fSAndrzej Pietrasiewicz /* Do we have at least as many strings as descriptors need? */
256700a2430fSAndrzej Pietrasiewicz needed_count = ffs->strings_count;
25688704fd73SGreg Kroah-Hartman if (str_count < needed_count)
256900a2430fSAndrzej Pietrasiewicz goto error;
257000a2430fSAndrzej Pietrasiewicz
257100a2430fSAndrzej Pietrasiewicz /*
257200a2430fSAndrzej Pietrasiewicz * If we don't need any strings just return and free all
257300a2430fSAndrzej Pietrasiewicz * memory.
257400a2430fSAndrzej Pietrasiewicz */
257500a2430fSAndrzej Pietrasiewicz if (!needed_count) {
257600a2430fSAndrzej Pietrasiewicz kfree(_data);
257700a2430fSAndrzej Pietrasiewicz return 0;
257800a2430fSAndrzej Pietrasiewicz }
257900a2430fSAndrzej Pietrasiewicz
258000a2430fSAndrzej Pietrasiewicz /* Allocate everything in one chunk so there's less maintenance. */
258100a2430fSAndrzej Pietrasiewicz {
258200a2430fSAndrzej Pietrasiewicz unsigned i = 0;
258300a2430fSAndrzej Pietrasiewicz vla_group(d);
258400a2430fSAndrzej Pietrasiewicz vla_item(d, struct usb_gadget_strings *, stringtabs,
2585f57004b9SDan Carpenter size_add(lang_count, 1));
258600a2430fSAndrzej Pietrasiewicz vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
258700a2430fSAndrzej Pietrasiewicz vla_item(d, struct usb_string, strings,
2588f57004b9SDan Carpenter size_mul(lang_count, (needed_count + 1)));
258900a2430fSAndrzej Pietrasiewicz
259000a2430fSAndrzej Pietrasiewicz char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
259100a2430fSAndrzej Pietrasiewicz
25928704fd73SGreg Kroah-Hartman if (!vlabuf) {
259300a2430fSAndrzej Pietrasiewicz kfree(_data);
259400a2430fSAndrzej Pietrasiewicz return -ENOMEM;
259500a2430fSAndrzej Pietrasiewicz }
259600a2430fSAndrzej Pietrasiewicz
259700a2430fSAndrzej Pietrasiewicz /* Initialize the VLA pointers */
259800a2430fSAndrzej Pietrasiewicz stringtabs = vla_ptr(vlabuf, d, stringtabs);
259900a2430fSAndrzej Pietrasiewicz t = vla_ptr(vlabuf, d, stringtab);
260000a2430fSAndrzej Pietrasiewicz i = lang_count;
260100a2430fSAndrzej Pietrasiewicz do {
260200a2430fSAndrzej Pietrasiewicz *stringtabs++ = t++;
260300a2430fSAndrzej Pietrasiewicz } while (--i);
260400a2430fSAndrzej Pietrasiewicz *stringtabs = NULL;
260500a2430fSAndrzej Pietrasiewicz
260600a2430fSAndrzej Pietrasiewicz /* stringtabs = vlabuf = d_stringtabs for later kfree */
260700a2430fSAndrzej Pietrasiewicz stringtabs = vla_ptr(vlabuf, d, stringtabs);
260800a2430fSAndrzej Pietrasiewicz t = vla_ptr(vlabuf, d, stringtab);
260900a2430fSAndrzej Pietrasiewicz s = vla_ptr(vlabuf, d, strings);
261000a2430fSAndrzej Pietrasiewicz }
261100a2430fSAndrzej Pietrasiewicz
261200a2430fSAndrzej Pietrasiewicz /* For each language */
261300a2430fSAndrzej Pietrasiewicz data += 16;
261400a2430fSAndrzej Pietrasiewicz len -= 16;
261500a2430fSAndrzej Pietrasiewicz
261600a2430fSAndrzej Pietrasiewicz do { /* lang_count > 0 so we can use do-while */
261700a2430fSAndrzej Pietrasiewicz unsigned needed = needed_count;
261855b74ce7SDean Anderson u32 str_per_lang = str_count;
261900a2430fSAndrzej Pietrasiewicz
26208704fd73SGreg Kroah-Hartman if (len < 3)
262100a2430fSAndrzej Pietrasiewicz goto error_free;
262200a2430fSAndrzej Pietrasiewicz t->language = get_unaligned_le16(data);
262300a2430fSAndrzej Pietrasiewicz t->strings = s;
262400a2430fSAndrzej Pietrasiewicz ++t;
262500a2430fSAndrzej Pietrasiewicz
262600a2430fSAndrzej Pietrasiewicz data += 2;
262700a2430fSAndrzej Pietrasiewicz len -= 2;
262800a2430fSAndrzej Pietrasiewicz
262900a2430fSAndrzej Pietrasiewicz /* For each string */
263000a2430fSAndrzej Pietrasiewicz do { /* str_count > 0 so we can use do-while */
263100a2430fSAndrzej Pietrasiewicz size_t length = strnlen(data, len);
263200a2430fSAndrzej Pietrasiewicz
26338704fd73SGreg Kroah-Hartman if (length == len)
263400a2430fSAndrzej Pietrasiewicz goto error_free;
263500a2430fSAndrzej Pietrasiewicz
263600a2430fSAndrzej Pietrasiewicz /*
263700a2430fSAndrzej Pietrasiewicz * User may provide more strings then we need,
263800a2430fSAndrzej Pietrasiewicz * if that's the case we simply ignore the
263900a2430fSAndrzej Pietrasiewicz * rest
264000a2430fSAndrzej Pietrasiewicz */
26418704fd73SGreg Kroah-Hartman if (needed) {
264200a2430fSAndrzej Pietrasiewicz /*
264300a2430fSAndrzej Pietrasiewicz * s->id will be set while adding
264400a2430fSAndrzej Pietrasiewicz * function to configuration so for
264500a2430fSAndrzej Pietrasiewicz * now just leave garbage here.
264600a2430fSAndrzej Pietrasiewicz */
264700a2430fSAndrzej Pietrasiewicz s->s = data;
264800a2430fSAndrzej Pietrasiewicz --needed;
264900a2430fSAndrzej Pietrasiewicz ++s;
265000a2430fSAndrzej Pietrasiewicz }
265100a2430fSAndrzej Pietrasiewicz
265200a2430fSAndrzej Pietrasiewicz data += length + 1;
265300a2430fSAndrzej Pietrasiewicz len -= length + 1;
265455b74ce7SDean Anderson } while (--str_per_lang);
265500a2430fSAndrzej Pietrasiewicz
265600a2430fSAndrzej Pietrasiewicz s->id = 0; /* terminator */
265700a2430fSAndrzej Pietrasiewicz s->s = NULL;
265800a2430fSAndrzej Pietrasiewicz ++s;
265900a2430fSAndrzej Pietrasiewicz
266000a2430fSAndrzej Pietrasiewicz } while (--lang_count);
266100a2430fSAndrzej Pietrasiewicz
266200a2430fSAndrzej Pietrasiewicz /* Some garbage left? */
26638704fd73SGreg Kroah-Hartman if (len)
266400a2430fSAndrzej Pietrasiewicz goto error_free;
266500a2430fSAndrzej Pietrasiewicz
266600a2430fSAndrzej Pietrasiewicz /* Done! */
266700a2430fSAndrzej Pietrasiewicz ffs->stringtabs = stringtabs;
266800a2430fSAndrzej Pietrasiewicz ffs->raw_strings = _data;
266900a2430fSAndrzej Pietrasiewicz
267000a2430fSAndrzej Pietrasiewicz return 0;
267100a2430fSAndrzej Pietrasiewicz
267200a2430fSAndrzej Pietrasiewicz error_free:
267300a2430fSAndrzej Pietrasiewicz kfree(stringtabs);
267400a2430fSAndrzej Pietrasiewicz error:
267500a2430fSAndrzej Pietrasiewicz kfree(_data);
267600a2430fSAndrzej Pietrasiewicz return -EINVAL;
267700a2430fSAndrzej Pietrasiewicz }
267800a2430fSAndrzej Pietrasiewicz
267900a2430fSAndrzej Pietrasiewicz
268000a2430fSAndrzej Pietrasiewicz /* Events handling and management *******************************************/
268100a2430fSAndrzej Pietrasiewicz
__ffs_event_add(struct ffs_data * ffs,enum usb_functionfs_event_type type)268200a2430fSAndrzej Pietrasiewicz static void __ffs_event_add(struct ffs_data *ffs,
268300a2430fSAndrzej Pietrasiewicz enum usb_functionfs_event_type type)
268400a2430fSAndrzej Pietrasiewicz {
268500a2430fSAndrzej Pietrasiewicz enum usb_functionfs_event_type rem_type1, rem_type2 = type;
268600a2430fSAndrzej Pietrasiewicz int neg = 0;
268700a2430fSAndrzej Pietrasiewicz
268800a2430fSAndrzej Pietrasiewicz /*
268900a2430fSAndrzej Pietrasiewicz * Abort any unhandled setup
269000a2430fSAndrzej Pietrasiewicz *
269100a2430fSAndrzej Pietrasiewicz * We do not need to worry about some cmpxchg() changing value
269200a2430fSAndrzej Pietrasiewicz * of ffs->setup_state without holding the lock because when
269300a2430fSAndrzej Pietrasiewicz * state is FFS_SETUP_PENDING cmpxchg() in several places in
269400a2430fSAndrzej Pietrasiewicz * the source does nothing.
269500a2430fSAndrzej Pietrasiewicz */
269600a2430fSAndrzej Pietrasiewicz if (ffs->setup_state == FFS_SETUP_PENDING)
269700a2430fSAndrzej Pietrasiewicz ffs->setup_state = FFS_SETUP_CANCELLED;
269800a2430fSAndrzej Pietrasiewicz
269967913bbdSMichal Nazarewicz /*
270067913bbdSMichal Nazarewicz * Logic of this function guarantees that there are at most four pending
270167913bbdSMichal Nazarewicz * evens on ffs->ev.types queue. This is important because the queue
270267913bbdSMichal Nazarewicz * has space for four elements only and __ffs_ep0_read_events function
270367913bbdSMichal Nazarewicz * depends on that limit as well. If more event types are added, those
270467913bbdSMichal Nazarewicz * limits have to be revisited or guaranteed to still hold.
270567913bbdSMichal Nazarewicz */
270600a2430fSAndrzej Pietrasiewicz switch (type) {
270700a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_RESUME:
270800a2430fSAndrzej Pietrasiewicz rem_type2 = FUNCTIONFS_SUSPEND;
2709a74005abSGustavo A. R. Silva fallthrough;
271000a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_SUSPEND:
271100a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_SETUP:
271200a2430fSAndrzej Pietrasiewicz rem_type1 = type;
271300a2430fSAndrzej Pietrasiewicz /* Discard all similar events */
271400a2430fSAndrzej Pietrasiewicz break;
271500a2430fSAndrzej Pietrasiewicz
271600a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_BIND:
271700a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_UNBIND:
271800a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_DISABLE:
271900a2430fSAndrzej Pietrasiewicz case FUNCTIONFS_ENABLE:
272000a2430fSAndrzej Pietrasiewicz /* Discard everything other then power management. */
272100a2430fSAndrzej Pietrasiewicz rem_type1 = FUNCTIONFS_SUSPEND;
272200a2430fSAndrzej Pietrasiewicz rem_type2 = FUNCTIONFS_RESUME;
272300a2430fSAndrzej Pietrasiewicz neg = 1;
272400a2430fSAndrzej Pietrasiewicz break;
272500a2430fSAndrzej Pietrasiewicz
272600a2430fSAndrzej Pietrasiewicz default:
2727fe00bcbfSMichal Nazarewicz WARN(1, "%d: unknown event, this should not happen\n", type);
2728fe00bcbfSMichal Nazarewicz return;
272900a2430fSAndrzej Pietrasiewicz }
273000a2430fSAndrzej Pietrasiewicz
273100a2430fSAndrzej Pietrasiewicz {
273200a2430fSAndrzej Pietrasiewicz u8 *ev = ffs->ev.types, *out = ev;
273300a2430fSAndrzej Pietrasiewicz unsigned n = ffs->ev.count;
273400a2430fSAndrzej Pietrasiewicz for (; n; --n, ++ev)
273500a2430fSAndrzej Pietrasiewicz if ((*ev == rem_type1 || *ev == rem_type2) == neg)
273600a2430fSAndrzej Pietrasiewicz *out++ = *ev;
273700a2430fSAndrzej Pietrasiewicz else
273800a2430fSAndrzej Pietrasiewicz pr_vdebug("purging event %d\n", *ev);
273900a2430fSAndrzej Pietrasiewicz ffs->ev.count = out - ffs->ev.types;
274000a2430fSAndrzej Pietrasiewicz }
274100a2430fSAndrzej Pietrasiewicz
274200a2430fSAndrzej Pietrasiewicz pr_vdebug("adding event %d\n", type);
274300a2430fSAndrzej Pietrasiewicz ffs->ev.types[ffs->ev.count++] = type;
274400a2430fSAndrzej Pietrasiewicz wake_up_locked(&ffs->ev.waitq);
27455e33f6fdSRobert Baldyga if (ffs->ffs_eventfd)
27465e33f6fdSRobert Baldyga eventfd_signal(ffs->ffs_eventfd, 1);
274700a2430fSAndrzej Pietrasiewicz }
274800a2430fSAndrzej Pietrasiewicz
ffs_event_add(struct ffs_data * ffs,enum usb_functionfs_event_type type)274900a2430fSAndrzej Pietrasiewicz static void ffs_event_add(struct ffs_data *ffs,
275000a2430fSAndrzej Pietrasiewicz enum usb_functionfs_event_type type)
275100a2430fSAndrzej Pietrasiewicz {
275200a2430fSAndrzej Pietrasiewicz unsigned long flags;
275300a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
275400a2430fSAndrzej Pietrasiewicz __ffs_event_add(ffs, type);
275500a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
275600a2430fSAndrzej Pietrasiewicz }
275700a2430fSAndrzej Pietrasiewicz
275800a2430fSAndrzej Pietrasiewicz /* Bind/unbind USB function hooks *******************************************/
275900a2430fSAndrzej Pietrasiewicz
ffs_ep_addr2idx(struct ffs_data * ffs,u8 endpoint_address)27606d5c1c77SRobert Baldyga static int ffs_ep_addr2idx(struct ffs_data *ffs, u8 endpoint_address)
27616d5c1c77SRobert Baldyga {
27626d5c1c77SRobert Baldyga int i;
27636d5c1c77SRobert Baldyga
27646d5c1c77SRobert Baldyga for (i = 1; i < ARRAY_SIZE(ffs->eps_addrmap); ++i)
27656d5c1c77SRobert Baldyga if (ffs->eps_addrmap[i] == endpoint_address)
27666d5c1c77SRobert Baldyga return i;
27676d5c1c77SRobert Baldyga return -ENOENT;
27686d5c1c77SRobert Baldyga }
27696d5c1c77SRobert Baldyga
__ffs_func_bind_do_descs(enum ffs_entity_type type,u8 * valuep,struct usb_descriptor_header * desc,void * priv)277000a2430fSAndrzej Pietrasiewicz static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
277100a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header *desc,
277200a2430fSAndrzej Pietrasiewicz void *priv)
277300a2430fSAndrzej Pietrasiewicz {
277400a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor *ds = (void *)desc;
277500a2430fSAndrzej Pietrasiewicz struct ffs_function *func = priv;
277600a2430fSAndrzej Pietrasiewicz struct ffs_ep *ffs_ep;
277785b06f5eSDan Carpenter unsigned ep_desc_id;
277885b06f5eSDan Carpenter int idx;
277900a2430fSAndrzej Pietrasiewicz static const char *speed_names[] = { "full", "high", "super" };
278000a2430fSAndrzej Pietrasiewicz
278100a2430fSAndrzej Pietrasiewicz if (type != FFS_DESCRIPTOR)
278200a2430fSAndrzej Pietrasiewicz return 0;
278300a2430fSAndrzej Pietrasiewicz
278400a2430fSAndrzej Pietrasiewicz /*
278500a2430fSAndrzej Pietrasiewicz * If ss_descriptors is not NULL, we are reading super speed
278600a2430fSAndrzej Pietrasiewicz * descriptors; if hs_descriptors is not NULL, we are reading high
278700a2430fSAndrzej Pietrasiewicz * speed descriptors; otherwise, we are reading full speed
278800a2430fSAndrzej Pietrasiewicz * descriptors.
278900a2430fSAndrzej Pietrasiewicz */
279000a2430fSAndrzej Pietrasiewicz if (func->function.ss_descriptors) {
279100a2430fSAndrzej Pietrasiewicz ep_desc_id = 2;
279200a2430fSAndrzej Pietrasiewicz func->function.ss_descriptors[(long)valuep] = desc;
279300a2430fSAndrzej Pietrasiewicz } else if (func->function.hs_descriptors) {
279400a2430fSAndrzej Pietrasiewicz ep_desc_id = 1;
279500a2430fSAndrzej Pietrasiewicz func->function.hs_descriptors[(long)valuep] = desc;
279600a2430fSAndrzej Pietrasiewicz } else {
279700a2430fSAndrzej Pietrasiewicz ep_desc_id = 0;
279800a2430fSAndrzej Pietrasiewicz func->function.fs_descriptors[(long)valuep] = desc;
279900a2430fSAndrzej Pietrasiewicz }
280000a2430fSAndrzej Pietrasiewicz
280100a2430fSAndrzej Pietrasiewicz if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
280200a2430fSAndrzej Pietrasiewicz return 0;
280300a2430fSAndrzej Pietrasiewicz
28046d5c1c77SRobert Baldyga idx = ffs_ep_addr2idx(func->ffs, ds->bEndpointAddress) - 1;
28056d5c1c77SRobert Baldyga if (idx < 0)
28066d5c1c77SRobert Baldyga return idx;
28076d5c1c77SRobert Baldyga
280800a2430fSAndrzej Pietrasiewicz ffs_ep = func->eps + idx;
280900a2430fSAndrzej Pietrasiewicz
28108704fd73SGreg Kroah-Hartman if (ffs_ep->descs[ep_desc_id]) {
281100a2430fSAndrzej Pietrasiewicz pr_err("two %sspeed descriptors for EP %d\n",
281200a2430fSAndrzej Pietrasiewicz speed_names[ep_desc_id],
281300a2430fSAndrzej Pietrasiewicz ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
281400a2430fSAndrzej Pietrasiewicz return -EINVAL;
281500a2430fSAndrzej Pietrasiewicz }
281600a2430fSAndrzej Pietrasiewicz ffs_ep->descs[ep_desc_id] = ds;
281700a2430fSAndrzej Pietrasiewicz
281800a2430fSAndrzej Pietrasiewicz ffs_dump_mem(": Original ep desc", ds, ds->bLength);
281900a2430fSAndrzej Pietrasiewicz if (ffs_ep->ep) {
282000a2430fSAndrzej Pietrasiewicz ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
282100a2430fSAndrzej Pietrasiewicz if (!ds->wMaxPacketSize)
282200a2430fSAndrzej Pietrasiewicz ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
282300a2430fSAndrzej Pietrasiewicz } else {
282400a2430fSAndrzej Pietrasiewicz struct usb_request *req;
282500a2430fSAndrzej Pietrasiewicz struct usb_ep *ep;
28261b0bf88fSRobert Baldyga u8 bEndpointAddress;
2827bdcc03ceSAndrzej Pietrasiewicz u16 wMaxPacketSize;
282800a2430fSAndrzej Pietrasiewicz
28291b0bf88fSRobert Baldyga /*
28301b0bf88fSRobert Baldyga * We back up bEndpointAddress because autoconfig overwrites
28311b0bf88fSRobert Baldyga * it with physical endpoint address.
28321b0bf88fSRobert Baldyga */
28331b0bf88fSRobert Baldyga bEndpointAddress = ds->bEndpointAddress;
2834bdcc03ceSAndrzej Pietrasiewicz /*
2835bdcc03ceSAndrzej Pietrasiewicz * We back up wMaxPacketSize because autoconfig treats
2836bdcc03ceSAndrzej Pietrasiewicz * endpoint descriptors as if they were full speed.
2837bdcc03ceSAndrzej Pietrasiewicz */
2838bdcc03ceSAndrzej Pietrasiewicz wMaxPacketSize = ds->wMaxPacketSize;
283900a2430fSAndrzej Pietrasiewicz pr_vdebug("autoconfig\n");
284000a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(func->gadget, ds);
28418704fd73SGreg Kroah-Hartman if (!ep)
284200a2430fSAndrzej Pietrasiewicz return -ENOTSUPP;
284300a2430fSAndrzej Pietrasiewicz ep->driver_data = func->eps + idx;
284400a2430fSAndrzej Pietrasiewicz
284500a2430fSAndrzej Pietrasiewicz req = usb_ep_alloc_request(ep, GFP_KERNEL);
28468704fd73SGreg Kroah-Hartman if (!req)
284700a2430fSAndrzej Pietrasiewicz return -ENOMEM;
284800a2430fSAndrzej Pietrasiewicz
284900a2430fSAndrzej Pietrasiewicz ffs_ep->ep = ep;
285000a2430fSAndrzej Pietrasiewicz ffs_ep->req = req;
285100a2430fSAndrzej Pietrasiewicz func->eps_revmap[ds->bEndpointAddress &
285200a2430fSAndrzej Pietrasiewicz USB_ENDPOINT_NUMBER_MASK] = idx + 1;
28531b0bf88fSRobert Baldyga /*
28541b0bf88fSRobert Baldyga * If we use virtual address mapping, we restore
28551b0bf88fSRobert Baldyga * original bEndpointAddress value.
28561b0bf88fSRobert Baldyga */
28571b0bf88fSRobert Baldyga if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
28581b0bf88fSRobert Baldyga ds->bEndpointAddress = bEndpointAddress;
2859bdcc03ceSAndrzej Pietrasiewicz /*
2860bdcc03ceSAndrzej Pietrasiewicz * Restore wMaxPacketSize which was potentially
2861bdcc03ceSAndrzej Pietrasiewicz * overwritten by autoconfig.
2862bdcc03ceSAndrzej Pietrasiewicz */
2863bdcc03ceSAndrzej Pietrasiewicz ds->wMaxPacketSize = wMaxPacketSize;
286400a2430fSAndrzej Pietrasiewicz }
286500a2430fSAndrzej Pietrasiewicz ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
286600a2430fSAndrzej Pietrasiewicz
286700a2430fSAndrzej Pietrasiewicz return 0;
286800a2430fSAndrzej Pietrasiewicz }
286900a2430fSAndrzej Pietrasiewicz
__ffs_func_bind_do_nums(enum ffs_entity_type type,u8 * valuep,struct usb_descriptor_header * desc,void * priv)287000a2430fSAndrzej Pietrasiewicz static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
287100a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header *desc,
287200a2430fSAndrzej Pietrasiewicz void *priv)
287300a2430fSAndrzej Pietrasiewicz {
287400a2430fSAndrzej Pietrasiewicz struct ffs_function *func = priv;
287500a2430fSAndrzej Pietrasiewicz unsigned idx;
287600a2430fSAndrzej Pietrasiewicz u8 newValue;
287700a2430fSAndrzej Pietrasiewicz
287800a2430fSAndrzej Pietrasiewicz switch (type) {
287900a2430fSAndrzej Pietrasiewicz default:
288000a2430fSAndrzej Pietrasiewicz case FFS_DESCRIPTOR:
288100a2430fSAndrzej Pietrasiewicz /* Handled in previous pass by __ffs_func_bind_do_descs() */
288200a2430fSAndrzej Pietrasiewicz return 0;
288300a2430fSAndrzej Pietrasiewicz
288400a2430fSAndrzej Pietrasiewicz case FFS_INTERFACE:
288500a2430fSAndrzej Pietrasiewicz idx = *valuep;
288600a2430fSAndrzej Pietrasiewicz if (func->interfaces_nums[idx] < 0) {
288700a2430fSAndrzej Pietrasiewicz int id = usb_interface_id(func->conf, &func->function);
28888704fd73SGreg Kroah-Hartman if (id < 0)
288900a2430fSAndrzej Pietrasiewicz return id;
289000a2430fSAndrzej Pietrasiewicz func->interfaces_nums[idx] = id;
289100a2430fSAndrzej Pietrasiewicz }
289200a2430fSAndrzej Pietrasiewicz newValue = func->interfaces_nums[idx];
289300a2430fSAndrzej Pietrasiewicz break;
289400a2430fSAndrzej Pietrasiewicz
289500a2430fSAndrzej Pietrasiewicz case FFS_STRING:
289600a2430fSAndrzej Pietrasiewicz /* String' IDs are allocated when fsf_data is bound to cdev */
289700a2430fSAndrzej Pietrasiewicz newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
289800a2430fSAndrzej Pietrasiewicz break;
289900a2430fSAndrzej Pietrasiewicz
290000a2430fSAndrzej Pietrasiewicz case FFS_ENDPOINT:
290100a2430fSAndrzej Pietrasiewicz /*
290200a2430fSAndrzej Pietrasiewicz * USB_DT_ENDPOINT are handled in
290300a2430fSAndrzej Pietrasiewicz * __ffs_func_bind_do_descs().
290400a2430fSAndrzej Pietrasiewicz */
290500a2430fSAndrzej Pietrasiewicz if (desc->bDescriptorType == USB_DT_ENDPOINT)
290600a2430fSAndrzej Pietrasiewicz return 0;
290700a2430fSAndrzej Pietrasiewicz
290800a2430fSAndrzej Pietrasiewicz idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
29098704fd73SGreg Kroah-Hartman if (!func->eps[idx].ep)
291000a2430fSAndrzej Pietrasiewicz return -EINVAL;
291100a2430fSAndrzej Pietrasiewicz
291200a2430fSAndrzej Pietrasiewicz {
291300a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor **descs;
291400a2430fSAndrzej Pietrasiewicz descs = func->eps[idx].descs;
291500a2430fSAndrzej Pietrasiewicz newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
291600a2430fSAndrzej Pietrasiewicz }
291700a2430fSAndrzej Pietrasiewicz break;
291800a2430fSAndrzej Pietrasiewicz }
291900a2430fSAndrzej Pietrasiewicz
292000a2430fSAndrzej Pietrasiewicz pr_vdebug("%02x -> %02x\n", *valuep, newValue);
292100a2430fSAndrzej Pietrasiewicz *valuep = newValue;
292200a2430fSAndrzej Pietrasiewicz return 0;
292300a2430fSAndrzej Pietrasiewicz }
292400a2430fSAndrzej Pietrasiewicz
__ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,struct usb_os_desc_header * h,void * data,unsigned len,void * priv)292500a2430fSAndrzej Pietrasiewicz static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
292600a2430fSAndrzej Pietrasiewicz struct usb_os_desc_header *h, void *data,
292700a2430fSAndrzej Pietrasiewicz unsigned len, void *priv)
292800a2430fSAndrzej Pietrasiewicz {
292900a2430fSAndrzej Pietrasiewicz struct ffs_function *func = priv;
293000a2430fSAndrzej Pietrasiewicz u8 length = 0;
293100a2430fSAndrzej Pietrasiewicz
293200a2430fSAndrzej Pietrasiewicz switch (type) {
293300a2430fSAndrzej Pietrasiewicz case FFS_OS_DESC_EXT_COMPAT: {
293400a2430fSAndrzej Pietrasiewicz struct usb_ext_compat_desc *desc = data;
293500a2430fSAndrzej Pietrasiewicz struct usb_os_desc_table *t;
293600a2430fSAndrzej Pietrasiewicz
293700a2430fSAndrzej Pietrasiewicz t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
293800a2430fSAndrzej Pietrasiewicz t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
293900a2430fSAndrzej Pietrasiewicz memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
294000a2430fSAndrzej Pietrasiewicz ARRAY_SIZE(desc->CompatibleID) +
294100a2430fSAndrzej Pietrasiewicz ARRAY_SIZE(desc->SubCompatibleID));
294200a2430fSAndrzej Pietrasiewicz length = sizeof(*desc);
294300a2430fSAndrzej Pietrasiewicz }
294400a2430fSAndrzej Pietrasiewicz break;
294500a2430fSAndrzej Pietrasiewicz case FFS_OS_DESC_EXT_PROP: {
294600a2430fSAndrzej Pietrasiewicz struct usb_ext_prop_desc *desc = data;
294700a2430fSAndrzej Pietrasiewicz struct usb_os_desc_table *t;
294800a2430fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop;
294900a2430fSAndrzej Pietrasiewicz char *ext_prop_name;
295000a2430fSAndrzej Pietrasiewicz char *ext_prop_data;
295100a2430fSAndrzej Pietrasiewicz
295200a2430fSAndrzej Pietrasiewicz t = &func->function.os_desc_table[h->interface];
295300a2430fSAndrzej Pietrasiewicz t->if_id = func->interfaces_nums[h->interface];
295400a2430fSAndrzej Pietrasiewicz
295500a2430fSAndrzej Pietrasiewicz ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
295600a2430fSAndrzej Pietrasiewicz func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
295700a2430fSAndrzej Pietrasiewicz
295800a2430fSAndrzej Pietrasiewicz ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
295900a2430fSAndrzej Pietrasiewicz ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
2960c40619bbSVincent Pelletier ext_prop->data_len = le32_to_cpu(*(__le32 *)
296100a2430fSAndrzej Pietrasiewicz usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
296200a2430fSAndrzej Pietrasiewicz length = ext_prop->name_len + ext_prop->data_len + 14;
296300a2430fSAndrzej Pietrasiewicz
296400a2430fSAndrzej Pietrasiewicz ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
296500a2430fSAndrzej Pietrasiewicz func->ffs->ms_os_descs_ext_prop_name_avail +=
296600a2430fSAndrzej Pietrasiewicz ext_prop->name_len;
296700a2430fSAndrzej Pietrasiewicz
296800a2430fSAndrzej Pietrasiewicz ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
296900a2430fSAndrzej Pietrasiewicz func->ffs->ms_os_descs_ext_prop_data_avail +=
297000a2430fSAndrzej Pietrasiewicz ext_prop->data_len;
297100a2430fSAndrzej Pietrasiewicz memcpy(ext_prop_data,
297200a2430fSAndrzej Pietrasiewicz usb_ext_prop_data_ptr(data, ext_prop->name_len),
297300a2430fSAndrzej Pietrasiewicz ext_prop->data_len);
297400a2430fSAndrzej Pietrasiewicz /* unicode data reported to the host as "WCHAR"s */
297500a2430fSAndrzej Pietrasiewicz switch (ext_prop->type) {
297600a2430fSAndrzej Pietrasiewicz case USB_EXT_PROP_UNICODE:
297700a2430fSAndrzej Pietrasiewicz case USB_EXT_PROP_UNICODE_ENV:
297800a2430fSAndrzej Pietrasiewicz case USB_EXT_PROP_UNICODE_LINK:
297900a2430fSAndrzej Pietrasiewicz case USB_EXT_PROP_UNICODE_MULTI:
298000a2430fSAndrzej Pietrasiewicz ext_prop->data_len *= 2;
298100a2430fSAndrzej Pietrasiewicz break;
298200a2430fSAndrzej Pietrasiewicz }
298300a2430fSAndrzej Pietrasiewicz ext_prop->data = ext_prop_data;
298400a2430fSAndrzej Pietrasiewicz
298500a2430fSAndrzej Pietrasiewicz memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
298600a2430fSAndrzej Pietrasiewicz ext_prop->name_len);
298700a2430fSAndrzej Pietrasiewicz /* property name reported to the host as "WCHAR"s */
298800a2430fSAndrzej Pietrasiewicz ext_prop->name_len *= 2;
298900a2430fSAndrzej Pietrasiewicz ext_prop->name = ext_prop_name;
299000a2430fSAndrzej Pietrasiewicz
299100a2430fSAndrzej Pietrasiewicz t->os_desc->ext_prop_len +=
299200a2430fSAndrzej Pietrasiewicz ext_prop->name_len + ext_prop->data_len + 14;
299300a2430fSAndrzej Pietrasiewicz ++t->os_desc->ext_prop_count;
299400a2430fSAndrzej Pietrasiewicz list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
299500a2430fSAndrzej Pietrasiewicz }
299600a2430fSAndrzej Pietrasiewicz break;
299700a2430fSAndrzej Pietrasiewicz default:
299800a2430fSAndrzej Pietrasiewicz pr_vdebug("unknown descriptor: %d\n", type);
299900a2430fSAndrzej Pietrasiewicz }
300000a2430fSAndrzej Pietrasiewicz
300100a2430fSAndrzej Pietrasiewicz return length;
300200a2430fSAndrzej Pietrasiewicz }
300300a2430fSAndrzej Pietrasiewicz
ffs_do_functionfs_bind(struct usb_function * f,struct usb_configuration * c)300400a2430fSAndrzej Pietrasiewicz static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
300500a2430fSAndrzej Pietrasiewicz struct usb_configuration *c)
300600a2430fSAndrzej Pietrasiewicz {
300700a2430fSAndrzej Pietrasiewicz struct ffs_function *func = ffs_func_from_usb(f);
300800a2430fSAndrzej Pietrasiewicz struct f_fs_opts *ffs_opts =
300900a2430fSAndrzej Pietrasiewicz container_of(f->fi, struct f_fs_opts, func_inst);
3010ecfbd7b9SAndrew Gabbasov struct ffs_data *ffs_data;
301100a2430fSAndrzej Pietrasiewicz int ret;
301200a2430fSAndrzej Pietrasiewicz
301300a2430fSAndrzej Pietrasiewicz /*
301400a2430fSAndrzej Pietrasiewicz * Legacy gadget triggers binding in functionfs_ready_callback,
301500a2430fSAndrzej Pietrasiewicz * which already uses locking; taking the same lock here would
301600a2430fSAndrzej Pietrasiewicz * cause a deadlock.
301700a2430fSAndrzej Pietrasiewicz *
301800a2430fSAndrzej Pietrasiewicz * Configfs-enabled gadgets however do need ffs_dev_lock.
301900a2430fSAndrzej Pietrasiewicz */
302000a2430fSAndrzej Pietrasiewicz if (!ffs_opts->no_configfs)
302100a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
302200a2430fSAndrzej Pietrasiewicz ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
3023ecfbd7b9SAndrew Gabbasov ffs_data = ffs_opts->dev->ffs_data;
302400a2430fSAndrzej Pietrasiewicz if (!ffs_opts->no_configfs)
302500a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
302600a2430fSAndrzej Pietrasiewicz if (ret)
302700a2430fSAndrzej Pietrasiewicz return ERR_PTR(ret);
302800a2430fSAndrzej Pietrasiewicz
3029ecfbd7b9SAndrew Gabbasov func->ffs = ffs_data;
303000a2430fSAndrzej Pietrasiewicz func->conf = c;
303100a2430fSAndrzej Pietrasiewicz func->gadget = c->cdev->gadget;
303200a2430fSAndrzej Pietrasiewicz
303300a2430fSAndrzej Pietrasiewicz /*
303400a2430fSAndrzej Pietrasiewicz * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
303500a2430fSAndrzej Pietrasiewicz * configurations are bound in sequence with list_for_each_entry,
303600a2430fSAndrzej Pietrasiewicz * in each configuration its functions are bound in sequence
303700a2430fSAndrzej Pietrasiewicz * with list_for_each_entry, so we assume no race condition
303800a2430fSAndrzej Pietrasiewicz * with regard to ffs_opts->bound access
303900a2430fSAndrzej Pietrasiewicz */
304000a2430fSAndrzej Pietrasiewicz if (!ffs_opts->refcnt) {
304100a2430fSAndrzej Pietrasiewicz ret = functionfs_bind(func->ffs, c->cdev);
304200a2430fSAndrzej Pietrasiewicz if (ret)
304300a2430fSAndrzej Pietrasiewicz return ERR_PTR(ret);
304400a2430fSAndrzej Pietrasiewicz }
304500a2430fSAndrzej Pietrasiewicz ffs_opts->refcnt++;
304600a2430fSAndrzej Pietrasiewicz func->function.strings = func->ffs->stringtabs;
304700a2430fSAndrzej Pietrasiewicz
304800a2430fSAndrzej Pietrasiewicz return ffs_opts;
304900a2430fSAndrzej Pietrasiewicz }
305000a2430fSAndrzej Pietrasiewicz
_ffs_func_bind(struct usb_configuration * c,struct usb_function * f)305100a2430fSAndrzej Pietrasiewicz static int _ffs_func_bind(struct usb_configuration *c,
305200a2430fSAndrzej Pietrasiewicz struct usb_function *f)
305300a2430fSAndrzej Pietrasiewicz {
305400a2430fSAndrzej Pietrasiewicz struct ffs_function *func = ffs_func_from_usb(f);
305500a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = func->ffs;
305600a2430fSAndrzej Pietrasiewicz
305700a2430fSAndrzej Pietrasiewicz const int full = !!func->ffs->fs_descs_count;
30586cf439e0SJack Pham const int high = !!func->ffs->hs_descs_count;
30596cf439e0SJack Pham const int super = !!func->ffs->ss_descs_count;
306000a2430fSAndrzej Pietrasiewicz
306100a2430fSAndrzej Pietrasiewicz int fs_len, hs_len, ss_len, ret, i;
30620015f915SDan Carpenter struct ffs_ep *eps_ptr;
306300a2430fSAndrzej Pietrasiewicz
306400a2430fSAndrzej Pietrasiewicz /* Make it a single chunk, less management later on */
306500a2430fSAndrzej Pietrasiewicz vla_group(d);
306600a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
306700a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
306800a2430fSAndrzej Pietrasiewicz full ? ffs->fs_descs_count + 1 : 0);
306900a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
307000a2430fSAndrzej Pietrasiewicz high ? ffs->hs_descs_count + 1 : 0);
307100a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
307200a2430fSAndrzej Pietrasiewicz super ? ffs->ss_descs_count + 1 : 0);
307300a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, short, inums, ffs->interfaces_count);
307400a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
307500a2430fSAndrzej Pietrasiewicz c->cdev->use_os_string ? ffs->interfaces_count : 0);
307600a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, char[16], ext_compat,
307700a2430fSAndrzej Pietrasiewicz c->cdev->use_os_string ? ffs->interfaces_count : 0);
307800a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, struct usb_os_desc, os_desc,
307900a2430fSAndrzej Pietrasiewicz c->cdev->use_os_string ? ffs->interfaces_count : 0);
308000a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
308100a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_count);
308200a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, char, ext_prop_name,
308300a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_name_len);
308400a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, char, ext_prop_data,
308500a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_data_len);
308600a2430fSAndrzej Pietrasiewicz vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
308700a2430fSAndrzej Pietrasiewicz char *vlabuf;
308800a2430fSAndrzej Pietrasiewicz
308900a2430fSAndrzej Pietrasiewicz /* Has descriptors only for speeds gadget does not support */
30908704fd73SGreg Kroah-Hartman if (!(full | high | super))
309100a2430fSAndrzej Pietrasiewicz return -ENOTSUPP;
309200a2430fSAndrzej Pietrasiewicz
309300a2430fSAndrzej Pietrasiewicz /* Allocate a single chunk, less management later on */
309400a2430fSAndrzej Pietrasiewicz vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
30958704fd73SGreg Kroah-Hartman if (!vlabuf)
309600a2430fSAndrzej Pietrasiewicz return -ENOMEM;
309700a2430fSAndrzej Pietrasiewicz
309800a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
309900a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_name_avail =
310000a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, ext_prop_name);
310100a2430fSAndrzej Pietrasiewicz ffs->ms_os_descs_ext_prop_data_avail =
310200a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, ext_prop_data);
310300a2430fSAndrzej Pietrasiewicz
310400a2430fSAndrzej Pietrasiewicz /* Copy descriptors */
310500a2430fSAndrzej Pietrasiewicz memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
310600a2430fSAndrzej Pietrasiewicz ffs->raw_descs_length);
310700a2430fSAndrzej Pietrasiewicz
310800a2430fSAndrzej Pietrasiewicz memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
31090015f915SDan Carpenter eps_ptr = vla_ptr(vlabuf, d, eps);
31100015f915SDan Carpenter for (i = 0; i < ffs->eps_count; i++)
31110015f915SDan Carpenter eps_ptr[i].num = -1;
311200a2430fSAndrzej Pietrasiewicz
311300a2430fSAndrzej Pietrasiewicz /* Save pointers
311400a2430fSAndrzej Pietrasiewicz * d_eps == vlabuf, func->eps used to kfree vlabuf later
311500a2430fSAndrzej Pietrasiewicz */
311600a2430fSAndrzej Pietrasiewicz func->eps = vla_ptr(vlabuf, d, eps);
311700a2430fSAndrzej Pietrasiewicz func->interfaces_nums = vla_ptr(vlabuf, d, inums);
311800a2430fSAndrzej Pietrasiewicz
311900a2430fSAndrzej Pietrasiewicz /*
312000a2430fSAndrzej Pietrasiewicz * Go through all the endpoint descriptors and allocate
312100a2430fSAndrzej Pietrasiewicz * endpoints first, so that later we can rewrite the endpoint
312200a2430fSAndrzej Pietrasiewicz * numbers without worrying that it may be described later on.
312300a2430fSAndrzej Pietrasiewicz */
31248704fd73SGreg Kroah-Hartman if (full) {
312500a2430fSAndrzej Pietrasiewicz func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
312600a2430fSAndrzej Pietrasiewicz fs_len = ffs_do_descs(ffs->fs_descs_count,
312700a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, raw_descs),
312800a2430fSAndrzej Pietrasiewicz d_raw_descs__sz,
312900a2430fSAndrzej Pietrasiewicz __ffs_func_bind_do_descs, func);
31308704fd73SGreg Kroah-Hartman if (fs_len < 0) {
313100a2430fSAndrzej Pietrasiewicz ret = fs_len;
313200a2430fSAndrzej Pietrasiewicz goto error;
313300a2430fSAndrzej Pietrasiewicz }
313400a2430fSAndrzej Pietrasiewicz } else {
313500a2430fSAndrzej Pietrasiewicz fs_len = 0;
313600a2430fSAndrzej Pietrasiewicz }
313700a2430fSAndrzej Pietrasiewicz
31388704fd73SGreg Kroah-Hartman if (high) {
313900a2430fSAndrzej Pietrasiewicz func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
314000a2430fSAndrzej Pietrasiewicz hs_len = ffs_do_descs(ffs->hs_descs_count,
314100a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, raw_descs) + fs_len,
314200a2430fSAndrzej Pietrasiewicz d_raw_descs__sz - fs_len,
314300a2430fSAndrzej Pietrasiewicz __ffs_func_bind_do_descs, func);
31448704fd73SGreg Kroah-Hartman if (hs_len < 0) {
314500a2430fSAndrzej Pietrasiewicz ret = hs_len;
314600a2430fSAndrzej Pietrasiewicz goto error;
314700a2430fSAndrzej Pietrasiewicz }
314800a2430fSAndrzej Pietrasiewicz } else {
314900a2430fSAndrzej Pietrasiewicz hs_len = 0;
315000a2430fSAndrzej Pietrasiewicz }
315100a2430fSAndrzej Pietrasiewicz
31528704fd73SGreg Kroah-Hartman if (super) {
3153a353397bSJack Pham func->function.ss_descriptors = func->function.ssp_descriptors =
3154a353397bSJack Pham vla_ptr(vlabuf, d, ss_descs);
315500a2430fSAndrzej Pietrasiewicz ss_len = ffs_do_descs(ffs->ss_descs_count,
315600a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
315700a2430fSAndrzej Pietrasiewicz d_raw_descs__sz - fs_len - hs_len,
315800a2430fSAndrzej Pietrasiewicz __ffs_func_bind_do_descs, func);
31598704fd73SGreg Kroah-Hartman if (ss_len < 0) {
316000a2430fSAndrzej Pietrasiewicz ret = ss_len;
316100a2430fSAndrzej Pietrasiewicz goto error;
316200a2430fSAndrzej Pietrasiewicz }
316300a2430fSAndrzej Pietrasiewicz } else {
316400a2430fSAndrzej Pietrasiewicz ss_len = 0;
316500a2430fSAndrzej Pietrasiewicz }
316600a2430fSAndrzej Pietrasiewicz
316700a2430fSAndrzej Pietrasiewicz /*
316800a2430fSAndrzej Pietrasiewicz * Now handle interface numbers allocation and interface and
316900a2430fSAndrzej Pietrasiewicz * endpoint numbers rewriting. We can do that in one go
317000a2430fSAndrzej Pietrasiewicz * now.
317100a2430fSAndrzej Pietrasiewicz */
317200a2430fSAndrzej Pietrasiewicz ret = ffs_do_descs(ffs->fs_descs_count +
317300a2430fSAndrzej Pietrasiewicz (high ? ffs->hs_descs_count : 0) +
317400a2430fSAndrzej Pietrasiewicz (super ? ffs->ss_descs_count : 0),
317500a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
317600a2430fSAndrzej Pietrasiewicz __ffs_func_bind_do_nums, func);
31778704fd73SGreg Kroah-Hartman if (ret < 0)
317800a2430fSAndrzej Pietrasiewicz goto error;
317900a2430fSAndrzej Pietrasiewicz
318000a2430fSAndrzej Pietrasiewicz func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
3181c6010c8bSJim Lin if (c->cdev->use_os_string) {
318200a2430fSAndrzej Pietrasiewicz for (i = 0; i < ffs->interfaces_count; ++i) {
318300a2430fSAndrzej Pietrasiewicz struct usb_os_desc *desc;
318400a2430fSAndrzej Pietrasiewicz
318500a2430fSAndrzej Pietrasiewicz desc = func->function.os_desc_table[i].os_desc =
318600a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, os_desc) +
318700a2430fSAndrzej Pietrasiewicz i * sizeof(struct usb_os_desc);
318800a2430fSAndrzej Pietrasiewicz desc->ext_compat_id =
318900a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, ext_compat) + i * 16;
319000a2430fSAndrzej Pietrasiewicz INIT_LIST_HEAD(&desc->ext_prop);
319100a2430fSAndrzej Pietrasiewicz }
319200a2430fSAndrzej Pietrasiewicz ret = ffs_do_os_descs(ffs->ms_os_descs_count,
319300a2430fSAndrzej Pietrasiewicz vla_ptr(vlabuf, d, raw_descs) +
319400a2430fSAndrzej Pietrasiewicz fs_len + hs_len + ss_len,
3195c6010c8bSJim Lin d_raw_descs__sz - fs_len - hs_len -
3196c6010c8bSJim Lin ss_len,
319700a2430fSAndrzej Pietrasiewicz __ffs_func_bind_do_os_desc, func);
31988704fd73SGreg Kroah-Hartman if (ret < 0)
319900a2430fSAndrzej Pietrasiewicz goto error;
3200c6010c8bSJim Lin }
320100a2430fSAndrzej Pietrasiewicz func->function.os_desc_n =
320200a2430fSAndrzej Pietrasiewicz c->cdev->use_os_string ? ffs->interfaces_count : 0;
320300a2430fSAndrzej Pietrasiewicz
320400a2430fSAndrzej Pietrasiewicz /* And we're done */
320500a2430fSAndrzej Pietrasiewicz ffs_event_add(ffs, FUNCTIONFS_BIND);
320600a2430fSAndrzej Pietrasiewicz return 0;
320700a2430fSAndrzej Pietrasiewicz
320800a2430fSAndrzej Pietrasiewicz error:
320900a2430fSAndrzej Pietrasiewicz /* XXX Do we need to release all claimed endpoints here? */
321000a2430fSAndrzej Pietrasiewicz return ret;
321100a2430fSAndrzej Pietrasiewicz }
321200a2430fSAndrzej Pietrasiewicz
ffs_func_bind(struct usb_configuration * c,struct usb_function * f)321300a2430fSAndrzej Pietrasiewicz static int ffs_func_bind(struct usb_configuration *c,
321400a2430fSAndrzej Pietrasiewicz struct usb_function *f)
321500a2430fSAndrzej Pietrasiewicz {
321600a2430fSAndrzej Pietrasiewicz struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
321755d81121SRobert Baldyga struct ffs_function *func = ffs_func_from_usb(f);
321855d81121SRobert Baldyga int ret;
321900a2430fSAndrzej Pietrasiewicz
322000a2430fSAndrzej Pietrasiewicz if (IS_ERR(ffs_opts))
322100a2430fSAndrzej Pietrasiewicz return PTR_ERR(ffs_opts);
322200a2430fSAndrzej Pietrasiewicz
322355d81121SRobert Baldyga ret = _ffs_func_bind(c, f);
322455d81121SRobert Baldyga if (ret && !--ffs_opts->refcnt)
322555d81121SRobert Baldyga functionfs_unbind(func->ffs);
322655d81121SRobert Baldyga
322755d81121SRobert Baldyga return ret;
322800a2430fSAndrzej Pietrasiewicz }
322900a2430fSAndrzej Pietrasiewicz
323000a2430fSAndrzej Pietrasiewicz
323100a2430fSAndrzej Pietrasiewicz /* Other USB function hooks *************************************************/
323200a2430fSAndrzej Pietrasiewicz
ffs_reset_work(struct work_struct * work)323318d6b32fSRobert Baldyga static void ffs_reset_work(struct work_struct *work)
323418d6b32fSRobert Baldyga {
323518d6b32fSRobert Baldyga struct ffs_data *ffs = container_of(work,
323618d6b32fSRobert Baldyga struct ffs_data, reset_work);
323718d6b32fSRobert Baldyga ffs_data_reset(ffs);
323818d6b32fSRobert Baldyga }
323918d6b32fSRobert Baldyga
ffs_func_set_alt(struct usb_function * f,unsigned interface,unsigned alt)324000a2430fSAndrzej Pietrasiewicz static int ffs_func_set_alt(struct usb_function *f,
324100a2430fSAndrzej Pietrasiewicz unsigned interface, unsigned alt)
324200a2430fSAndrzej Pietrasiewicz {
324300a2430fSAndrzej Pietrasiewicz struct ffs_function *func = ffs_func_from_usb(f);
324400a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = func->ffs;
324500a2430fSAndrzej Pietrasiewicz int ret = 0, intf;
324600a2430fSAndrzej Pietrasiewicz
324700a2430fSAndrzej Pietrasiewicz if (alt != (unsigned)-1) {
324800a2430fSAndrzej Pietrasiewicz intf = ffs_func_revmap_intf(func, interface);
32498704fd73SGreg Kroah-Hartman if (intf < 0)
325000a2430fSAndrzej Pietrasiewicz return intf;
325100a2430fSAndrzej Pietrasiewicz }
325200a2430fSAndrzej Pietrasiewicz
325300a2430fSAndrzej Pietrasiewicz if (ffs->func)
325400a2430fSAndrzej Pietrasiewicz ffs_func_eps_disable(ffs->func);
325500a2430fSAndrzej Pietrasiewicz
325618d6b32fSRobert Baldyga if (ffs->state == FFS_DEACTIVATED) {
325718d6b32fSRobert Baldyga ffs->state = FFS_CLOSING;
325818d6b32fSRobert Baldyga INIT_WORK(&ffs->reset_work, ffs_reset_work);
325918d6b32fSRobert Baldyga schedule_work(&ffs->reset_work);
326018d6b32fSRobert Baldyga return -ENODEV;
326118d6b32fSRobert Baldyga }
326218d6b32fSRobert Baldyga
326300a2430fSAndrzej Pietrasiewicz if (ffs->state != FFS_ACTIVE)
326400a2430fSAndrzej Pietrasiewicz return -ENODEV;
326500a2430fSAndrzej Pietrasiewicz
326600a2430fSAndrzej Pietrasiewicz if (alt == (unsigned)-1) {
326700a2430fSAndrzej Pietrasiewicz ffs->func = NULL;
326800a2430fSAndrzej Pietrasiewicz ffs_event_add(ffs, FUNCTIONFS_DISABLE);
326900a2430fSAndrzej Pietrasiewicz return 0;
327000a2430fSAndrzej Pietrasiewicz }
327100a2430fSAndrzej Pietrasiewicz
327200a2430fSAndrzej Pietrasiewicz ffs->func = func;
327300a2430fSAndrzej Pietrasiewicz ret = ffs_func_eps_enable(func);
32748704fd73SGreg Kroah-Hartman if (ret >= 0)
327500a2430fSAndrzej Pietrasiewicz ffs_event_add(ffs, FUNCTIONFS_ENABLE);
327600a2430fSAndrzej Pietrasiewicz return ret;
327700a2430fSAndrzej Pietrasiewicz }
327800a2430fSAndrzej Pietrasiewicz
ffs_func_disable(struct usb_function * f)327900a2430fSAndrzej Pietrasiewicz static void ffs_func_disable(struct usb_function *f)
328000a2430fSAndrzej Pietrasiewicz {
328100a2430fSAndrzej Pietrasiewicz ffs_func_set_alt(f, 0, (unsigned)-1);
328200a2430fSAndrzej Pietrasiewicz }
328300a2430fSAndrzej Pietrasiewicz
ffs_func_setup(struct usb_function * f,const struct usb_ctrlrequest * creq)328400a2430fSAndrzej Pietrasiewicz static int ffs_func_setup(struct usb_function *f,
328500a2430fSAndrzej Pietrasiewicz const struct usb_ctrlrequest *creq)
328600a2430fSAndrzej Pietrasiewicz {
328700a2430fSAndrzej Pietrasiewicz struct ffs_function *func = ffs_func_from_usb(f);
328800a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = func->ffs;
328900a2430fSAndrzej Pietrasiewicz unsigned long flags;
329000a2430fSAndrzej Pietrasiewicz int ret;
329100a2430fSAndrzej Pietrasiewicz
329200a2430fSAndrzej Pietrasiewicz pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
329300a2430fSAndrzej Pietrasiewicz pr_vdebug("creq->bRequest = %02x\n", creq->bRequest);
329400a2430fSAndrzej Pietrasiewicz pr_vdebug("creq->wValue = %04x\n", le16_to_cpu(creq->wValue));
329500a2430fSAndrzej Pietrasiewicz pr_vdebug("creq->wIndex = %04x\n", le16_to_cpu(creq->wIndex));
329600a2430fSAndrzej Pietrasiewicz pr_vdebug("creq->wLength = %04x\n", le16_to_cpu(creq->wLength));
329700a2430fSAndrzej Pietrasiewicz
329800a2430fSAndrzej Pietrasiewicz /*
329900a2430fSAndrzej Pietrasiewicz * Most requests directed to interface go through here
330000a2430fSAndrzej Pietrasiewicz * (notable exceptions are set/get interface) so we need to
330100a2430fSAndrzej Pietrasiewicz * handle them. All other either handled by composite or
330200a2430fSAndrzej Pietrasiewicz * passed to usb_configuration->setup() (if one is set). No
330300a2430fSAndrzej Pietrasiewicz * matter, we will handle requests directed to endpoint here
330454dfce6dSFelix Hädicke * as well (as it's straightforward). Other request recipient
330554dfce6dSFelix Hädicke * types are only handled when the user flag FUNCTIONFS_ALL_CTRL_RECIP
330654dfce6dSFelix Hädicke * is being used.
330700a2430fSAndrzej Pietrasiewicz */
330800a2430fSAndrzej Pietrasiewicz if (ffs->state != FFS_ACTIVE)
330900a2430fSAndrzej Pietrasiewicz return -ENODEV;
331000a2430fSAndrzej Pietrasiewicz
331100a2430fSAndrzej Pietrasiewicz switch (creq->bRequestType & USB_RECIP_MASK) {
331200a2430fSAndrzej Pietrasiewicz case USB_RECIP_INTERFACE:
331300a2430fSAndrzej Pietrasiewicz ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
33148704fd73SGreg Kroah-Hartman if (ret < 0)
331500a2430fSAndrzej Pietrasiewicz return ret;
331600a2430fSAndrzej Pietrasiewicz break;
331700a2430fSAndrzej Pietrasiewicz
331800a2430fSAndrzej Pietrasiewicz case USB_RECIP_ENDPOINT:
331900a2430fSAndrzej Pietrasiewicz ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
33208704fd73SGreg Kroah-Hartman if (ret < 0)
332100a2430fSAndrzej Pietrasiewicz return ret;
33221b0bf88fSRobert Baldyga if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
33231b0bf88fSRobert Baldyga ret = func->ffs->eps_addrmap[ret];
332400a2430fSAndrzej Pietrasiewicz break;
332500a2430fSAndrzej Pietrasiewicz
332600a2430fSAndrzej Pietrasiewicz default:
332754dfce6dSFelix Hädicke if (func->ffs->user_flags & FUNCTIONFS_ALL_CTRL_RECIP)
332854dfce6dSFelix Hädicke ret = le16_to_cpu(creq->wIndex);
332954dfce6dSFelix Hädicke else
333000a2430fSAndrzej Pietrasiewicz return -EOPNOTSUPP;
333100a2430fSAndrzej Pietrasiewicz }
333200a2430fSAndrzej Pietrasiewicz
333300a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
333400a2430fSAndrzej Pietrasiewicz ffs->ev.setup = *creq;
333500a2430fSAndrzej Pietrasiewicz ffs->ev.setup.wIndex = cpu_to_le16(ret);
333600a2430fSAndrzej Pietrasiewicz __ffs_event_add(ffs, FUNCTIONFS_SETUP);
333700a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
333800a2430fSAndrzej Pietrasiewicz
33391a15d6e2SChris Wulff return ffs->ev.setup.wLength == 0 ? USB_GADGET_DELAYED_STATUS : 0;
334000a2430fSAndrzej Pietrasiewicz }
334100a2430fSAndrzej Pietrasiewicz
ffs_func_req_match(struct usb_function * f,const struct usb_ctrlrequest * creq,bool config0)334254dfce6dSFelix Hädicke static bool ffs_func_req_match(struct usb_function *f,
33431a00b457SFelix Hädicke const struct usb_ctrlrequest *creq,
33441a00b457SFelix Hädicke bool config0)
334554dfce6dSFelix Hädicke {
334654dfce6dSFelix Hädicke struct ffs_function *func = ffs_func_from_usb(f);
334754dfce6dSFelix Hädicke
33484368c28aSFelix Hädicke if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP))
33491a00b457SFelix Hädicke return false;
33501a00b457SFelix Hädicke
335154dfce6dSFelix Hädicke switch (creq->bRequestType & USB_RECIP_MASK) {
335254dfce6dSFelix Hädicke case USB_RECIP_INTERFACE:
335305e78c69SFelix Hädicke return (ffs_func_revmap_intf(func,
335405e78c69SFelix Hädicke le16_to_cpu(creq->wIndex)) >= 0);
335554dfce6dSFelix Hädicke case USB_RECIP_ENDPOINT:
335605e78c69SFelix Hädicke return (ffs_func_revmap_ep(func,
335705e78c69SFelix Hädicke le16_to_cpu(creq->wIndex)) >= 0);
335854dfce6dSFelix Hädicke default:
335954dfce6dSFelix Hädicke return (bool) (func->ffs->user_flags &
336054dfce6dSFelix Hädicke FUNCTIONFS_ALL_CTRL_RECIP);
336154dfce6dSFelix Hädicke }
336254dfce6dSFelix Hädicke }
336354dfce6dSFelix Hädicke
ffs_func_suspend(struct usb_function * f)336400a2430fSAndrzej Pietrasiewicz static void ffs_func_suspend(struct usb_function *f)
336500a2430fSAndrzej Pietrasiewicz {
336600a2430fSAndrzej Pietrasiewicz ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
336700a2430fSAndrzej Pietrasiewicz }
336800a2430fSAndrzej Pietrasiewicz
ffs_func_resume(struct usb_function * f)336900a2430fSAndrzej Pietrasiewicz static void ffs_func_resume(struct usb_function *f)
337000a2430fSAndrzej Pietrasiewicz {
337100a2430fSAndrzej Pietrasiewicz ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
337200a2430fSAndrzej Pietrasiewicz }
337300a2430fSAndrzej Pietrasiewicz
337400a2430fSAndrzej Pietrasiewicz
337500a2430fSAndrzej Pietrasiewicz /* Endpoint and interface numbers reverse mapping ***************************/
337600a2430fSAndrzej Pietrasiewicz
ffs_func_revmap_ep(struct ffs_function * func,u8 num)337700a2430fSAndrzej Pietrasiewicz static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
337800a2430fSAndrzej Pietrasiewicz {
337900a2430fSAndrzej Pietrasiewicz num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
338000a2430fSAndrzej Pietrasiewicz return num ? num : -EDOM;
338100a2430fSAndrzej Pietrasiewicz }
338200a2430fSAndrzej Pietrasiewicz
ffs_func_revmap_intf(struct ffs_function * func,u8 intf)338300a2430fSAndrzej Pietrasiewicz static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
338400a2430fSAndrzej Pietrasiewicz {
338500a2430fSAndrzej Pietrasiewicz short *nums = func->interfaces_nums;
338600a2430fSAndrzej Pietrasiewicz unsigned count = func->ffs->interfaces_count;
338700a2430fSAndrzej Pietrasiewicz
338800a2430fSAndrzej Pietrasiewicz for (; count; --count, ++nums) {
338900a2430fSAndrzej Pietrasiewicz if (*nums >= 0 && *nums == intf)
339000a2430fSAndrzej Pietrasiewicz return nums - func->interfaces_nums;
339100a2430fSAndrzej Pietrasiewicz }
339200a2430fSAndrzej Pietrasiewicz
339300a2430fSAndrzej Pietrasiewicz return -EDOM;
339400a2430fSAndrzej Pietrasiewicz }
339500a2430fSAndrzej Pietrasiewicz
339600a2430fSAndrzej Pietrasiewicz
339700a2430fSAndrzej Pietrasiewicz /* Devices management *******************************************************/
339800a2430fSAndrzej Pietrasiewicz
339900a2430fSAndrzej Pietrasiewicz static LIST_HEAD(ffs_devices);
340000a2430fSAndrzej Pietrasiewicz
_ffs_do_find_dev(const char * name)340100a2430fSAndrzej Pietrasiewicz static struct ffs_dev *_ffs_do_find_dev(const char *name)
340200a2430fSAndrzej Pietrasiewicz {
340300a2430fSAndrzej Pietrasiewicz struct ffs_dev *dev;
340400a2430fSAndrzej Pietrasiewicz
3405ea920bb4SMichal Nazarewicz if (!name)
3406ea920bb4SMichal Nazarewicz return NULL;
3407ea920bb4SMichal Nazarewicz
340800a2430fSAndrzej Pietrasiewicz list_for_each_entry(dev, &ffs_devices, entry) {
340900a2430fSAndrzej Pietrasiewicz if (strcmp(dev->name, name) == 0)
341000a2430fSAndrzej Pietrasiewicz return dev;
341100a2430fSAndrzej Pietrasiewicz }
341200a2430fSAndrzej Pietrasiewicz
341300a2430fSAndrzej Pietrasiewicz return NULL;
341400a2430fSAndrzej Pietrasiewicz }
341500a2430fSAndrzej Pietrasiewicz
341600a2430fSAndrzej Pietrasiewicz /*
341700a2430fSAndrzej Pietrasiewicz * ffs_lock must be taken by the caller of this function
341800a2430fSAndrzej Pietrasiewicz */
_ffs_get_single_dev(void)341900a2430fSAndrzej Pietrasiewicz static struct ffs_dev *_ffs_get_single_dev(void)
342000a2430fSAndrzej Pietrasiewicz {
342100a2430fSAndrzej Pietrasiewicz struct ffs_dev *dev;
342200a2430fSAndrzej Pietrasiewicz
342300a2430fSAndrzej Pietrasiewicz if (list_is_singular(&ffs_devices)) {
342400a2430fSAndrzej Pietrasiewicz dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
342500a2430fSAndrzej Pietrasiewicz if (dev->single)
342600a2430fSAndrzej Pietrasiewicz return dev;
342700a2430fSAndrzej Pietrasiewicz }
342800a2430fSAndrzej Pietrasiewicz
342900a2430fSAndrzej Pietrasiewicz return NULL;
343000a2430fSAndrzej Pietrasiewicz }
343100a2430fSAndrzej Pietrasiewicz
343200a2430fSAndrzej Pietrasiewicz /*
343300a2430fSAndrzej Pietrasiewicz * ffs_lock must be taken by the caller of this function
343400a2430fSAndrzej Pietrasiewicz */
_ffs_find_dev(const char * name)343500a2430fSAndrzej Pietrasiewicz static struct ffs_dev *_ffs_find_dev(const char *name)
343600a2430fSAndrzej Pietrasiewicz {
343700a2430fSAndrzej Pietrasiewicz struct ffs_dev *dev;
343800a2430fSAndrzej Pietrasiewicz
343900a2430fSAndrzej Pietrasiewicz dev = _ffs_get_single_dev();
344000a2430fSAndrzej Pietrasiewicz if (dev)
344100a2430fSAndrzej Pietrasiewicz return dev;
344200a2430fSAndrzej Pietrasiewicz
344300a2430fSAndrzej Pietrasiewicz return _ffs_do_find_dev(name);
344400a2430fSAndrzej Pietrasiewicz }
344500a2430fSAndrzej Pietrasiewicz
344600a2430fSAndrzej Pietrasiewicz /* Configfs support *********************************************************/
344700a2430fSAndrzej Pietrasiewicz
to_ffs_opts(struct config_item * item)344800a2430fSAndrzej Pietrasiewicz static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
344900a2430fSAndrzej Pietrasiewicz {
345000a2430fSAndrzej Pietrasiewicz return container_of(to_config_group(item), struct f_fs_opts,
345100a2430fSAndrzej Pietrasiewicz func_inst.group);
345200a2430fSAndrzej Pietrasiewicz }
345300a2430fSAndrzej Pietrasiewicz
ffs_attr_release(struct config_item * item)345400a2430fSAndrzej Pietrasiewicz static void ffs_attr_release(struct config_item *item)
345500a2430fSAndrzej Pietrasiewicz {
345600a2430fSAndrzej Pietrasiewicz struct f_fs_opts *opts = to_ffs_opts(item);
345700a2430fSAndrzej Pietrasiewicz
345800a2430fSAndrzej Pietrasiewicz usb_put_function_instance(&opts->func_inst);
345900a2430fSAndrzej Pietrasiewicz }
346000a2430fSAndrzej Pietrasiewicz
346100a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations ffs_item_ops = {
346200a2430fSAndrzej Pietrasiewicz .release = ffs_attr_release,
346300a2430fSAndrzej Pietrasiewicz };
346400a2430fSAndrzej Pietrasiewicz
346597363902SBhumika Goyal static const struct config_item_type ffs_func_type = {
346600a2430fSAndrzej Pietrasiewicz .ct_item_ops = &ffs_item_ops,
346700a2430fSAndrzej Pietrasiewicz .ct_owner = THIS_MODULE,
346800a2430fSAndrzej Pietrasiewicz };
346900a2430fSAndrzej Pietrasiewicz
347000a2430fSAndrzej Pietrasiewicz
347100a2430fSAndrzej Pietrasiewicz /* Function registration interface ******************************************/
347200a2430fSAndrzej Pietrasiewicz
ffs_free_inst(struct usb_function_instance * f)347300a2430fSAndrzej Pietrasiewicz static void ffs_free_inst(struct usb_function_instance *f)
347400a2430fSAndrzej Pietrasiewicz {
347500a2430fSAndrzej Pietrasiewicz struct f_fs_opts *opts;
347600a2430fSAndrzej Pietrasiewicz
347700a2430fSAndrzej Pietrasiewicz opts = to_f_fs_opts(f);
3478ecfbd7b9SAndrew Gabbasov ffs_release_dev(opts->dev);
347900a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
348000a2430fSAndrzej Pietrasiewicz _ffs_free_dev(opts->dev);
348100a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
348200a2430fSAndrzej Pietrasiewicz kfree(opts);
348300a2430fSAndrzej Pietrasiewicz }
348400a2430fSAndrzej Pietrasiewicz
ffs_set_inst_name(struct usb_function_instance * fi,const char * name)348500a2430fSAndrzej Pietrasiewicz static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
348600a2430fSAndrzej Pietrasiewicz {
3487c593642cSPankaj Bharadiya if (strlen(name) >= sizeof_field(struct ffs_dev, name))
348800a2430fSAndrzej Pietrasiewicz return -ENAMETOOLONG;
3489ea920bb4SMichal Nazarewicz return ffs_name_dev(to_f_fs_opts(fi)->dev, name);
349000a2430fSAndrzej Pietrasiewicz }
349100a2430fSAndrzej Pietrasiewicz
ffs_alloc_inst(void)349200a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *ffs_alloc_inst(void)
349300a2430fSAndrzej Pietrasiewicz {
349400a2430fSAndrzej Pietrasiewicz struct f_fs_opts *opts;
349500a2430fSAndrzej Pietrasiewicz struct ffs_dev *dev;
349600a2430fSAndrzej Pietrasiewicz
349700a2430fSAndrzej Pietrasiewicz opts = kzalloc(sizeof(*opts), GFP_KERNEL);
349800a2430fSAndrzej Pietrasiewicz if (!opts)
349900a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
350000a2430fSAndrzej Pietrasiewicz
350100a2430fSAndrzej Pietrasiewicz opts->func_inst.set_inst_name = ffs_set_inst_name;
350200a2430fSAndrzej Pietrasiewicz opts->func_inst.free_func_inst = ffs_free_inst;
350300a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
350400a2430fSAndrzej Pietrasiewicz dev = _ffs_alloc_dev();
350500a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
350600a2430fSAndrzej Pietrasiewicz if (IS_ERR(dev)) {
350700a2430fSAndrzej Pietrasiewicz kfree(opts);
350800a2430fSAndrzej Pietrasiewicz return ERR_CAST(dev);
350900a2430fSAndrzej Pietrasiewicz }
351000a2430fSAndrzej Pietrasiewicz opts->dev = dev;
351100a2430fSAndrzej Pietrasiewicz dev->opts = opts;
351200a2430fSAndrzej Pietrasiewicz
351300a2430fSAndrzej Pietrasiewicz config_group_init_type_name(&opts->func_inst.group, "",
351400a2430fSAndrzej Pietrasiewicz &ffs_func_type);
351500a2430fSAndrzej Pietrasiewicz return &opts->func_inst;
351600a2430fSAndrzej Pietrasiewicz }
351700a2430fSAndrzej Pietrasiewicz
ffs_free(struct usb_function * f)351800a2430fSAndrzej Pietrasiewicz static void ffs_free(struct usb_function *f)
351900a2430fSAndrzej Pietrasiewicz {
352000a2430fSAndrzej Pietrasiewicz kfree(ffs_func_from_usb(f));
352100a2430fSAndrzej Pietrasiewicz }
352200a2430fSAndrzej Pietrasiewicz
ffs_func_unbind(struct usb_configuration * c,struct usb_function * f)352300a2430fSAndrzej Pietrasiewicz static void ffs_func_unbind(struct usb_configuration *c,
352400a2430fSAndrzej Pietrasiewicz struct usb_function *f)
352500a2430fSAndrzej Pietrasiewicz {
352600a2430fSAndrzej Pietrasiewicz struct ffs_function *func = ffs_func_from_usb(f);
352700a2430fSAndrzej Pietrasiewicz struct ffs_data *ffs = func->ffs;
352800a2430fSAndrzej Pietrasiewicz struct f_fs_opts *opts =
352900a2430fSAndrzej Pietrasiewicz container_of(f->fi, struct f_fs_opts, func_inst);
353000a2430fSAndrzej Pietrasiewicz struct ffs_ep *ep = func->eps;
353100a2430fSAndrzej Pietrasiewicz unsigned count = ffs->eps_count;
353200a2430fSAndrzej Pietrasiewicz unsigned long flags;
353300a2430fSAndrzej Pietrasiewicz
353400a2430fSAndrzej Pietrasiewicz if (ffs->func == func) {
353500a2430fSAndrzej Pietrasiewicz ffs_func_eps_disable(func);
353600a2430fSAndrzej Pietrasiewicz ffs->func = NULL;
353700a2430fSAndrzej Pietrasiewicz }
353800a2430fSAndrzej Pietrasiewicz
35396fc1db5eSWesley Cheng /* Drain any pending AIO completions */
35406fc1db5eSWesley Cheng drain_workqueue(ffs->io_completion_wq);
35416fc1db5eSWesley Cheng
3542efb6b535SUttkarsh Aggarwal ffs_event_add(ffs, FUNCTIONFS_UNBIND);
354300a2430fSAndrzej Pietrasiewicz if (!--opts->refcnt)
354400a2430fSAndrzej Pietrasiewicz functionfs_unbind(ffs);
354500a2430fSAndrzej Pietrasiewicz
354600a2430fSAndrzej Pietrasiewicz /* cleanup after autoconfig */
354700a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&func->ffs->eps_lock, flags);
354808f37148SVincent Pelletier while (count--) {
354900a2430fSAndrzej Pietrasiewicz if (ep->ep && ep->req)
355000a2430fSAndrzej Pietrasiewicz usb_ep_free_request(ep->ep, ep->req);
355100a2430fSAndrzej Pietrasiewicz ep->req = NULL;
355200a2430fSAndrzej Pietrasiewicz ++ep;
355308f37148SVincent Pelletier }
355400a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
355500a2430fSAndrzej Pietrasiewicz kfree(func->eps);
355600a2430fSAndrzej Pietrasiewicz func->eps = NULL;
355700a2430fSAndrzej Pietrasiewicz /*
355800a2430fSAndrzej Pietrasiewicz * eps, descriptors and interfaces_nums are allocated in the
355900a2430fSAndrzej Pietrasiewicz * same chunk so only one free is required.
356000a2430fSAndrzej Pietrasiewicz */
356100a2430fSAndrzej Pietrasiewicz func->function.fs_descriptors = NULL;
356200a2430fSAndrzej Pietrasiewicz func->function.hs_descriptors = NULL;
356300a2430fSAndrzej Pietrasiewicz func->function.ss_descriptors = NULL;
3564a353397bSJack Pham func->function.ssp_descriptors = NULL;
356500a2430fSAndrzej Pietrasiewicz func->interfaces_nums = NULL;
356600a2430fSAndrzej Pietrasiewicz
356700a2430fSAndrzej Pietrasiewicz }
356800a2430fSAndrzej Pietrasiewicz
ffs_alloc(struct usb_function_instance * fi)356900a2430fSAndrzej Pietrasiewicz static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
357000a2430fSAndrzej Pietrasiewicz {
357100a2430fSAndrzej Pietrasiewicz struct ffs_function *func;
357200a2430fSAndrzej Pietrasiewicz
357300a2430fSAndrzej Pietrasiewicz func = kzalloc(sizeof(*func), GFP_KERNEL);
35748704fd73SGreg Kroah-Hartman if (!func)
357500a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
357600a2430fSAndrzej Pietrasiewicz
357700a2430fSAndrzej Pietrasiewicz func->function.name = "Function FS Gadget";
357800a2430fSAndrzej Pietrasiewicz
357900a2430fSAndrzej Pietrasiewicz func->function.bind = ffs_func_bind;
358000a2430fSAndrzej Pietrasiewicz func->function.unbind = ffs_func_unbind;
358100a2430fSAndrzej Pietrasiewicz func->function.set_alt = ffs_func_set_alt;
358200a2430fSAndrzej Pietrasiewicz func->function.disable = ffs_func_disable;
358300a2430fSAndrzej Pietrasiewicz func->function.setup = ffs_func_setup;
358454dfce6dSFelix Hädicke func->function.req_match = ffs_func_req_match;
358500a2430fSAndrzej Pietrasiewicz func->function.suspend = ffs_func_suspend;
358600a2430fSAndrzej Pietrasiewicz func->function.resume = ffs_func_resume;
358700a2430fSAndrzej Pietrasiewicz func->function.free_func = ffs_free;
358800a2430fSAndrzej Pietrasiewicz
358900a2430fSAndrzej Pietrasiewicz return &func->function;
359000a2430fSAndrzej Pietrasiewicz }
359100a2430fSAndrzej Pietrasiewicz
359200a2430fSAndrzej Pietrasiewicz /*
359300a2430fSAndrzej Pietrasiewicz * ffs_lock must be taken by the caller of this function
359400a2430fSAndrzej Pietrasiewicz */
_ffs_alloc_dev(void)359500a2430fSAndrzej Pietrasiewicz static struct ffs_dev *_ffs_alloc_dev(void)
359600a2430fSAndrzej Pietrasiewicz {
359700a2430fSAndrzej Pietrasiewicz struct ffs_dev *dev;
359800a2430fSAndrzej Pietrasiewicz int ret;
359900a2430fSAndrzej Pietrasiewicz
360000a2430fSAndrzej Pietrasiewicz if (_ffs_get_single_dev())
360100a2430fSAndrzej Pietrasiewicz return ERR_PTR(-EBUSY);
360200a2430fSAndrzej Pietrasiewicz
360300a2430fSAndrzej Pietrasiewicz dev = kzalloc(sizeof(*dev), GFP_KERNEL);
360400a2430fSAndrzej Pietrasiewicz if (!dev)
360500a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
360600a2430fSAndrzej Pietrasiewicz
360700a2430fSAndrzej Pietrasiewicz if (list_empty(&ffs_devices)) {
360800a2430fSAndrzej Pietrasiewicz ret = functionfs_init();
360900a2430fSAndrzej Pietrasiewicz if (ret) {
361000a2430fSAndrzej Pietrasiewicz kfree(dev);
361100a2430fSAndrzej Pietrasiewicz return ERR_PTR(ret);
361200a2430fSAndrzej Pietrasiewicz }
361300a2430fSAndrzej Pietrasiewicz }
361400a2430fSAndrzej Pietrasiewicz
361500a2430fSAndrzej Pietrasiewicz list_add(&dev->entry, &ffs_devices);
361600a2430fSAndrzej Pietrasiewicz
361700a2430fSAndrzej Pietrasiewicz return dev;
361800a2430fSAndrzej Pietrasiewicz }
361900a2430fSAndrzej Pietrasiewicz
ffs_name_dev(struct ffs_dev * dev,const char * name)362000a2430fSAndrzej Pietrasiewicz int ffs_name_dev(struct ffs_dev *dev, const char *name)
362100a2430fSAndrzej Pietrasiewicz {
3622ea920bb4SMichal Nazarewicz struct ffs_dev *existing;
3623ea920bb4SMichal Nazarewicz int ret = 0;
362400a2430fSAndrzej Pietrasiewicz
362500a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
3626ea920bb4SMichal Nazarewicz
3627ea920bb4SMichal Nazarewicz existing = _ffs_do_find_dev(name);
3628ea920bb4SMichal Nazarewicz if (!existing)
3629b7db5733SWolfram Sang strscpy(dev->name, name, ARRAY_SIZE(dev->name));
3630ea920bb4SMichal Nazarewicz else if (existing != dev)
3631ea920bb4SMichal Nazarewicz ret = -EBUSY;
3632ea920bb4SMichal Nazarewicz
363300a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
363400a2430fSAndrzej Pietrasiewicz
363500a2430fSAndrzej Pietrasiewicz return ret;
363600a2430fSAndrzej Pietrasiewicz }
363700a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(ffs_name_dev);
363800a2430fSAndrzej Pietrasiewicz
ffs_single_dev(struct ffs_dev * dev)363900a2430fSAndrzej Pietrasiewicz int ffs_single_dev(struct ffs_dev *dev)
364000a2430fSAndrzej Pietrasiewicz {
364100a2430fSAndrzej Pietrasiewicz int ret;
364200a2430fSAndrzej Pietrasiewicz
364300a2430fSAndrzej Pietrasiewicz ret = 0;
364400a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
364500a2430fSAndrzej Pietrasiewicz
364600a2430fSAndrzej Pietrasiewicz if (!list_is_singular(&ffs_devices))
364700a2430fSAndrzej Pietrasiewicz ret = -EBUSY;
364800a2430fSAndrzej Pietrasiewicz else
364900a2430fSAndrzej Pietrasiewicz dev->single = true;
365000a2430fSAndrzej Pietrasiewicz
365100a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
365200a2430fSAndrzej Pietrasiewicz return ret;
365300a2430fSAndrzej Pietrasiewicz }
365400a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(ffs_single_dev);
365500a2430fSAndrzej Pietrasiewicz
365600a2430fSAndrzej Pietrasiewicz /*
365700a2430fSAndrzej Pietrasiewicz * ffs_lock must be taken by the caller of this function
365800a2430fSAndrzej Pietrasiewicz */
_ffs_free_dev(struct ffs_dev * dev)365900a2430fSAndrzej Pietrasiewicz static void _ffs_free_dev(struct ffs_dev *dev)
366000a2430fSAndrzej Pietrasiewicz {
366100a2430fSAndrzej Pietrasiewicz list_del(&dev->entry);
36623262ad82SJim Baxter
366300a2430fSAndrzej Pietrasiewicz kfree(dev);
366400a2430fSAndrzej Pietrasiewicz if (list_empty(&ffs_devices))
366500a2430fSAndrzej Pietrasiewicz functionfs_cleanup();
366600a2430fSAndrzej Pietrasiewicz }
366700a2430fSAndrzej Pietrasiewicz
ffs_acquire_dev(const char * dev_name,struct ffs_data * ffs_data)3668ecfbd7b9SAndrew Gabbasov static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data)
366900a2430fSAndrzej Pietrasiewicz {
3670ecfbd7b9SAndrew Gabbasov int ret = 0;
367100a2430fSAndrzej Pietrasiewicz struct ffs_dev *ffs_dev;
367200a2430fSAndrzej Pietrasiewicz
367300a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
367400a2430fSAndrzej Pietrasiewicz
367500a2430fSAndrzej Pietrasiewicz ffs_dev = _ffs_find_dev(dev_name);
3676ecfbd7b9SAndrew Gabbasov if (!ffs_dev) {
3677ecfbd7b9SAndrew Gabbasov ret = -ENOENT;
3678ecfbd7b9SAndrew Gabbasov } else if (ffs_dev->mounted) {
3679ecfbd7b9SAndrew Gabbasov ret = -EBUSY;
3680ecfbd7b9SAndrew Gabbasov } else if (ffs_dev->ffs_acquire_dev_callback &&
3681ecfbd7b9SAndrew Gabbasov ffs_dev->ffs_acquire_dev_callback(ffs_dev)) {
3682ecfbd7b9SAndrew Gabbasov ret = -ENOENT;
3683ecfbd7b9SAndrew Gabbasov } else {
368400a2430fSAndrzej Pietrasiewicz ffs_dev->mounted = true;
3685ecfbd7b9SAndrew Gabbasov ffs_dev->ffs_data = ffs_data;
3686ecfbd7b9SAndrew Gabbasov ffs_data->private_data = ffs_dev;
368700a2430fSAndrzej Pietrasiewicz }
368800a2430fSAndrzej Pietrasiewicz
3689ecfbd7b9SAndrew Gabbasov ffs_dev_unlock();
3690ecfbd7b9SAndrew Gabbasov return ret;
3691ecfbd7b9SAndrew Gabbasov }
369200a2430fSAndrzej Pietrasiewicz
ffs_release_dev(struct ffs_dev * ffs_dev)3693ecfbd7b9SAndrew Gabbasov static void ffs_release_dev(struct ffs_dev *ffs_dev)
3694ecfbd7b9SAndrew Gabbasov {
369500a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
369600a2430fSAndrzej Pietrasiewicz
3697ecfbd7b9SAndrew Gabbasov if (ffs_dev && ffs_dev->mounted) {
369800a2430fSAndrzej Pietrasiewicz ffs_dev->mounted = false;
3699ecfbd7b9SAndrew Gabbasov if (ffs_dev->ffs_data) {
3700ecfbd7b9SAndrew Gabbasov ffs_dev->ffs_data->private_data = NULL;
3701ecfbd7b9SAndrew Gabbasov ffs_dev->ffs_data = NULL;
3702ecfbd7b9SAndrew Gabbasov }
370300a2430fSAndrzej Pietrasiewicz
370400a2430fSAndrzej Pietrasiewicz if (ffs_dev->ffs_release_dev_callback)
370500a2430fSAndrzej Pietrasiewicz ffs_dev->ffs_release_dev_callback(ffs_dev);
370600a2430fSAndrzej Pietrasiewicz }
370700a2430fSAndrzej Pietrasiewicz
370800a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
370900a2430fSAndrzej Pietrasiewicz }
371000a2430fSAndrzej Pietrasiewicz
ffs_ready(struct ffs_data * ffs)371100a2430fSAndrzej Pietrasiewicz static int ffs_ready(struct ffs_data *ffs)
371200a2430fSAndrzej Pietrasiewicz {
371300a2430fSAndrzej Pietrasiewicz struct ffs_dev *ffs_obj;
371400a2430fSAndrzej Pietrasiewicz int ret = 0;
371500a2430fSAndrzej Pietrasiewicz
371600a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
371700a2430fSAndrzej Pietrasiewicz
371800a2430fSAndrzej Pietrasiewicz ffs_obj = ffs->private_data;
371900a2430fSAndrzej Pietrasiewicz if (!ffs_obj) {
372000a2430fSAndrzej Pietrasiewicz ret = -EINVAL;
372100a2430fSAndrzej Pietrasiewicz goto done;
372200a2430fSAndrzej Pietrasiewicz }
372300a2430fSAndrzej Pietrasiewicz if (WARN_ON(ffs_obj->desc_ready)) {
372400a2430fSAndrzej Pietrasiewicz ret = -EBUSY;
372500a2430fSAndrzej Pietrasiewicz goto done;
372600a2430fSAndrzej Pietrasiewicz }
372700a2430fSAndrzej Pietrasiewicz
372800a2430fSAndrzej Pietrasiewicz ffs_obj->desc_ready = true;
372900a2430fSAndrzej Pietrasiewicz
373049a79d8bSKrzysztof Opasiak if (ffs_obj->ffs_ready_callback) {
373100a2430fSAndrzej Pietrasiewicz ret = ffs_obj->ffs_ready_callback(ffs);
373249a79d8bSKrzysztof Opasiak if (ret)
373349a79d8bSKrzysztof Opasiak goto done;
373449a79d8bSKrzysztof Opasiak }
373500a2430fSAndrzej Pietrasiewicz
373649a79d8bSKrzysztof Opasiak set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
373700a2430fSAndrzej Pietrasiewicz done:
373800a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
373900a2430fSAndrzej Pietrasiewicz return ret;
374000a2430fSAndrzej Pietrasiewicz }
374100a2430fSAndrzej Pietrasiewicz
ffs_closed(struct ffs_data * ffs)374200a2430fSAndrzej Pietrasiewicz static void ffs_closed(struct ffs_data *ffs)
374300a2430fSAndrzej Pietrasiewicz {
374400a2430fSAndrzej Pietrasiewicz struct ffs_dev *ffs_obj;
3745f14e9ad1SRui Miguel Silva struct f_fs_opts *opts;
3746b3ce3ce0SBaolin Wang struct config_item *ci;
374700a2430fSAndrzej Pietrasiewicz
374800a2430fSAndrzej Pietrasiewicz ffs_dev_lock();
374900a2430fSAndrzej Pietrasiewicz
375000a2430fSAndrzej Pietrasiewicz ffs_obj = ffs->private_data;
375100a2430fSAndrzej Pietrasiewicz if (!ffs_obj)
375200a2430fSAndrzej Pietrasiewicz goto done;
375300a2430fSAndrzej Pietrasiewicz
375400a2430fSAndrzej Pietrasiewicz ffs_obj->desc_ready = false;
375500a2430fSAndrzej Pietrasiewicz
375649a79d8bSKrzysztof Opasiak if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
375749a79d8bSKrzysztof Opasiak ffs_obj->ffs_closed_callback)
375800a2430fSAndrzej Pietrasiewicz ffs_obj->ffs_closed_callback(ffs);
375900a2430fSAndrzej Pietrasiewicz
3760f14e9ad1SRui Miguel Silva if (ffs_obj->opts)
3761f14e9ad1SRui Miguel Silva opts = ffs_obj->opts;
3762f14e9ad1SRui Miguel Silva else
3763f14e9ad1SRui Miguel Silva goto done;
3764f14e9ad1SRui Miguel Silva
3765f14e9ad1SRui Miguel Silva if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
37662c935bc5SPeter Zijlstra || !kref_read(&opts->func_inst.group.cg_item.ci_kref))
376700a2430fSAndrzej Pietrasiewicz goto done;
376800a2430fSAndrzej Pietrasiewicz
3769b3ce3ce0SBaolin Wang ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
3770b3ce3ce0SBaolin Wang ffs_dev_unlock();
3771b3ce3ce0SBaolin Wang
3772ce5bf9a5SHemant Kumar if (test_bit(FFS_FL_BOUND, &ffs->flags))
3773b3ce3ce0SBaolin Wang unregister_gadget_item(ci);
3774b3ce3ce0SBaolin Wang return;
377500a2430fSAndrzej Pietrasiewicz done:
377600a2430fSAndrzej Pietrasiewicz ffs_dev_unlock();
377700a2430fSAndrzej Pietrasiewicz }
377800a2430fSAndrzej Pietrasiewicz
377900a2430fSAndrzej Pietrasiewicz /* Misc helper functions ****************************************************/
378000a2430fSAndrzej Pietrasiewicz
ffs_mutex_lock(struct mutex * mutex,unsigned nonblock)378100a2430fSAndrzej Pietrasiewicz static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
378200a2430fSAndrzej Pietrasiewicz {
378300a2430fSAndrzej Pietrasiewicz return nonblock
37848704fd73SGreg Kroah-Hartman ? mutex_trylock(mutex) ? 0 : -EAGAIN
378500a2430fSAndrzej Pietrasiewicz : mutex_lock_interruptible(mutex);
378600a2430fSAndrzej Pietrasiewicz }
378700a2430fSAndrzej Pietrasiewicz
ffs_prepare_buffer(const char __user * buf,size_t len)378800a2430fSAndrzej Pietrasiewicz static char *ffs_prepare_buffer(const char __user *buf, size_t len)
378900a2430fSAndrzej Pietrasiewicz {
379000a2430fSAndrzej Pietrasiewicz char *data;
379100a2430fSAndrzej Pietrasiewicz
37928704fd73SGreg Kroah-Hartman if (!len)
379300a2430fSAndrzej Pietrasiewicz return NULL;
379400a2430fSAndrzej Pietrasiewicz
379542343207Skernel test robot data = memdup_user(buf, len);
379642343207Skernel test robot if (IS_ERR(data))
379790059e93SSalah Triki return data;
379800a2430fSAndrzej Pietrasiewicz
379900a2430fSAndrzej Pietrasiewicz pr_vdebug("Buffer from user space:\n");
380000a2430fSAndrzej Pietrasiewicz ffs_dump_mem("", data, len);
380100a2430fSAndrzej Pietrasiewicz
380200a2430fSAndrzej Pietrasiewicz return data;
380300a2430fSAndrzej Pietrasiewicz }
380400a2430fSAndrzej Pietrasiewicz
380500a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
380600a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
380700a2430fSAndrzej Pietrasiewicz MODULE_AUTHOR("Michal Nazarewicz");
3808