1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * PlayStation 2 Trance Vibrator driver |
4 | * |
5 | * Copyright (C) 2006 Sam Hocevar <sam@zoy.org> |
6 | */ |
7 | |
8 | /* Standard include files */ |
9 | #include <linux/kernel.h> |
10 | #include <linux/errno.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/module.h> |
13 | #include <linux/usb.h> |
14 | |
15 | #define DRIVER_AUTHOR "Sam Hocevar, sam@zoy.org" |
16 | #define DRIVER_DESC "PlayStation 2 Trance Vibrator driver" |
17 | |
18 | #define TRANCEVIBRATOR_VENDOR_ID 0x0b49 /* ASCII Corporation */ |
19 | #define TRANCEVIBRATOR_PRODUCT_ID 0x064f /* Trance Vibrator */ |
20 | |
21 | static const struct usb_device_id id_table[] = { |
22 | { USB_DEVICE(TRANCEVIBRATOR_VENDOR_ID, TRANCEVIBRATOR_PRODUCT_ID) }, |
23 | { }, |
24 | }; |
25 | MODULE_DEVICE_TABLE (usb, id_table); |
26 | |
27 | /* Driver-local specific stuff */ |
28 | struct trancevibrator { |
29 | struct usb_device *udev; |
30 | unsigned int speed; |
31 | }; |
32 | |
33 | static ssize_t speed_show(struct device *dev, struct device_attribute *attr, |
34 | char *buf) |
35 | { |
36 | struct usb_interface *intf = to_usb_interface(dev); |
37 | struct trancevibrator *tv = usb_get_intfdata(intf); |
38 | |
39 | return sprintf(buf, fmt: "%d\n" , tv->speed); |
40 | } |
41 | |
42 | static ssize_t speed_store(struct device *dev, struct device_attribute *attr, |
43 | const char *buf, size_t count) |
44 | { |
45 | struct usb_interface *intf = to_usb_interface(dev); |
46 | struct trancevibrator *tv = usb_get_intfdata(intf); |
47 | int temp, retval, old; |
48 | |
49 | retval = kstrtoint(s: buf, base: 10, res: &temp); |
50 | if (retval) |
51 | return retval; |
52 | if (temp > 255) |
53 | temp = 255; |
54 | else if (temp < 0) |
55 | temp = 0; |
56 | old = tv->speed; |
57 | tv->speed = temp; |
58 | |
59 | dev_dbg(&tv->udev->dev, "speed = %d\n" , tv->speed); |
60 | |
61 | /* Set speed */ |
62 | retval = usb_control_msg(dev: tv->udev, usb_sndctrlpipe(tv->udev, 0), |
63 | request: 0x01, /* vendor request: set speed */ |
64 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, |
65 | value: tv->speed, /* speed value */ |
66 | index: 0, NULL, size: 0, USB_CTRL_SET_TIMEOUT); |
67 | if (retval) { |
68 | tv->speed = old; |
69 | dev_dbg(&tv->udev->dev, "retval = %d\n" , retval); |
70 | return retval; |
71 | } |
72 | return count; |
73 | } |
74 | static DEVICE_ATTR_RW(speed); |
75 | |
76 | static struct attribute *tv_attrs[] = { |
77 | &dev_attr_speed.attr, |
78 | NULL, |
79 | }; |
80 | ATTRIBUTE_GROUPS(tv); |
81 | |
82 | static int tv_probe(struct usb_interface *interface, |
83 | const struct usb_device_id *id) |
84 | { |
85 | struct usb_device *udev = interface_to_usbdev(interface); |
86 | struct trancevibrator *dev; |
87 | int retval; |
88 | |
89 | dev = kzalloc(size: sizeof(struct trancevibrator), GFP_KERNEL); |
90 | if (!dev) { |
91 | retval = -ENOMEM; |
92 | goto error; |
93 | } |
94 | |
95 | dev->udev = usb_get_dev(dev: udev); |
96 | usb_set_intfdata(intf: interface, data: dev); |
97 | |
98 | return 0; |
99 | |
100 | error: |
101 | kfree(objp: dev); |
102 | return retval; |
103 | } |
104 | |
105 | static void tv_disconnect(struct usb_interface *interface) |
106 | { |
107 | struct trancevibrator *dev; |
108 | |
109 | dev = usb_get_intfdata (intf: interface); |
110 | usb_set_intfdata(intf: interface, NULL); |
111 | usb_put_dev(dev: dev->udev); |
112 | kfree(objp: dev); |
113 | } |
114 | |
115 | /* USB subsystem object */ |
116 | static struct usb_driver tv_driver = { |
117 | .name = "trancevibrator" , |
118 | .probe = tv_probe, |
119 | .disconnect = tv_disconnect, |
120 | .id_table = id_table, |
121 | .dev_groups = tv_groups, |
122 | }; |
123 | |
124 | module_usb_driver(tv_driver); |
125 | |
126 | MODULE_AUTHOR(DRIVER_AUTHOR); |
127 | MODULE_DESCRIPTION(DRIVER_DESC); |
128 | MODULE_LICENSE("GPL" ); |
129 | |