1 // SPDX-License-Identifier: GPL-2.0 2 /* -*- linux-c -*- 3 * Cypress USB Thermometer driver 4 * 5 * Copyright (c) 2004 Erik Rigtorp <erkki@linux.nu> <erik@rigtorp.com> 6 * 7 * This driver works with Elektor magazine USB Interface as published in 8 * issue #291. It should also work with the original starter kit/demo board 9 * from Cypress. 10 */ 11 12 13 #include <linux/kernel.h> 14 #include <linux/errno.h> 15 #include <linux/slab.h> 16 #include <linux/module.h> 17 #include <linux/usb.h> 18 19 #define DRIVER_AUTHOR "Erik Rigtorp" 20 #define DRIVER_DESC "Cypress USB Thermometer driver" 21 22 #define USB_SKEL_VENDOR_ID 0x04b4 23 #define USB_SKEL_PRODUCT_ID 0x0002 24 25 static const struct usb_device_id id_table[] = { 26 { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, 27 { } 28 }; 29 MODULE_DEVICE_TABLE (usb, id_table); 30 31 /* Structure to hold all of our device specific stuff */ 32 struct usb_cytherm { 33 struct usb_device *udev; /* save off the usb device pointer */ 34 struct usb_interface *interface; /* the interface for this device */ 35 int brightness; 36 }; 37 38 39 /* local function prototypes */ 40 static int cytherm_probe(struct usb_interface *interface, 41 const struct usb_device_id *id); 42 static void cytherm_disconnect(struct usb_interface *interface); 43 44 45 /* usb specific object needed to register this driver with the usb subsystem */ 46 static struct usb_driver cytherm_driver = { 47 .name = "cytherm", 48 .probe = cytherm_probe, 49 .disconnect = cytherm_disconnect, 50 .id_table = id_table, 51 }; 52 53 /* Vendor requests */ 54 /* They all operate on one byte at a time */ 55 #define PING 0x00 56 #define READ_ROM 0x01 /* Reads form ROM, value = address */ 57 #define READ_RAM 0x02 /* Reads form RAM, value = address */ 58 #define WRITE_RAM 0x03 /* Write to RAM, value = address, index = data */ 59 #define READ_PORT 0x04 /* Reads from port, value = address */ 60 #define WRITE_PORT 0x05 /* Write to port, value = address, index = data */ 61 62 63 /* Send a vendor command to device */ 64 static int vendor_command(struct usb_device *dev, unsigned char request, 65 unsigned char value, unsigned char index, 66 void *buf, int size) 67 { 68 return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 69 request, 70 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, 71 value, 72 index, buf, size, 73 USB_CTRL_GET_TIMEOUT); 74 } 75 76 77 78 #define BRIGHTNESS 0x2c /* RAM location for brightness value */ 79 #define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */ 80 81 static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf) 82 { 83 struct usb_interface *intf = to_usb_interface(dev); 84 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 85 86 return sprintf(buf, "%i", cytherm->brightness); 87 } 88 89 static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, 90 size_t count) 91 { 92 struct usb_interface *intf = to_usb_interface(dev); 93 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 94 95 unsigned char *buffer; 96 int retval; 97 98 buffer = kmalloc(8, GFP_KERNEL); 99 if (!buffer) 100 return 0; 101 102 cytherm->brightness = simple_strtoul(buf, NULL, 10); 103 104 if (cytherm->brightness > 0xFF) 105 cytherm->brightness = 0xFF; 106 else if (cytherm->brightness < 0) 107 cytherm->brightness = 0; 108 109 /* Set brightness */ 110 retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS, 111 cytherm->brightness, buffer, 8); 112 if (retval) 113 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 114 /* Inform µC that we have changed the brightness setting */ 115 retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS_SEM, 116 0x01, buffer, 8); 117 if (retval) 118 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 119 120 kfree(buffer); 121 122 return count; 123 } 124 static DEVICE_ATTR_RW(brightness); 125 126 127 #define TEMP 0x33 /* RAM location for temperature */ 128 #define SIGN 0x34 /* RAM location for temperature sign */ 129 130 static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf) 131 { 132 133 struct usb_interface *intf = to_usb_interface(dev); 134 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 135 136 int retval; 137 unsigned char *buffer; 138 139 int temp, sign; 140 141 buffer = kmalloc(8, GFP_KERNEL); 142 if (!buffer) 143 return 0; 144 145 /* read temperature */ 146 retval = vendor_command(cytherm->udev, READ_RAM, TEMP, 0, buffer, 8); 147 if (retval) 148 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 149 temp = buffer[1]; 150 151 /* read sign */ 152 retval = vendor_command(cytherm->udev, READ_RAM, SIGN, 0, buffer, 8); 153 if (retval) 154 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 155 sign = buffer[1]; 156 157 kfree(buffer); 158 159 return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1, 160 5*(temp - ((temp >> 1) << 1))); 161 } 162 static DEVICE_ATTR_RO(temp); 163 164 165 #define BUTTON 0x7a 166 167 static ssize_t button_show(struct device *dev, struct device_attribute *attr, char *buf) 168 { 169 170 struct usb_interface *intf = to_usb_interface(dev); 171 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 172 173 int retval; 174 unsigned char *buffer; 175 176 buffer = kmalloc(8, GFP_KERNEL); 177 if (!buffer) 178 return 0; 179 180 /* check button */ 181 retval = vendor_command(cytherm->udev, READ_RAM, BUTTON, 0, buffer, 8); 182 if (retval) 183 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 184 185 retval = buffer[1]; 186 187 kfree(buffer); 188 189 if (retval) 190 return sprintf(buf, "1"); 191 else 192 return sprintf(buf, "0"); 193 } 194 static DEVICE_ATTR_RO(button); 195 196 197 static ssize_t port0_show(struct device *dev, struct device_attribute *attr, char *buf) 198 { 199 struct usb_interface *intf = to_usb_interface(dev); 200 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 201 202 int retval; 203 unsigned char *buffer; 204 205 buffer = kmalloc(8, GFP_KERNEL); 206 if (!buffer) 207 return 0; 208 209 retval = vendor_command(cytherm->udev, READ_PORT, 0, 0, buffer, 8); 210 if (retval) 211 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 212 213 retval = buffer[1]; 214 215 kfree(buffer); 216 217 return sprintf(buf, "%d", retval); 218 } 219 220 221 static ssize_t port0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 222 { 223 struct usb_interface *intf = to_usb_interface(dev); 224 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 225 226 unsigned char *buffer; 227 int retval; 228 int tmp; 229 230 buffer = kmalloc(8, GFP_KERNEL); 231 if (!buffer) 232 return 0; 233 234 tmp = simple_strtoul(buf, NULL, 10); 235 236 if (tmp > 0xFF) 237 tmp = 0xFF; 238 else if (tmp < 0) 239 tmp = 0; 240 241 retval = vendor_command(cytherm->udev, WRITE_PORT, 0, 242 tmp, buffer, 8); 243 if (retval) 244 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 245 246 kfree(buffer); 247 248 return count; 249 } 250 static DEVICE_ATTR_RW(port0); 251 252 static ssize_t port1_show(struct device *dev, struct device_attribute *attr, char *buf) 253 { 254 struct usb_interface *intf = to_usb_interface(dev); 255 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 256 257 int retval; 258 unsigned char *buffer; 259 260 buffer = kmalloc(8, GFP_KERNEL); 261 if (!buffer) 262 return 0; 263 264 retval = vendor_command(cytherm->udev, READ_PORT, 1, 0, buffer, 8); 265 if (retval) 266 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 267 268 retval = buffer[1]; 269 270 kfree(buffer); 271 272 return sprintf(buf, "%d", retval); 273 } 274 275 276 static ssize_t port1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 277 { 278 struct usb_interface *intf = to_usb_interface(dev); 279 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 280 281 unsigned char *buffer; 282 int retval; 283 int tmp; 284 285 buffer = kmalloc(8, GFP_KERNEL); 286 if (!buffer) 287 return 0; 288 289 tmp = simple_strtoul(buf, NULL, 10); 290 291 if (tmp > 0xFF) 292 tmp = 0xFF; 293 else if (tmp < 0) 294 tmp = 0; 295 296 retval = vendor_command(cytherm->udev, WRITE_PORT, 1, 297 tmp, buffer, 8); 298 if (retval) 299 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 300 301 kfree(buffer); 302 303 return count; 304 } 305 static DEVICE_ATTR_RW(port1); 306 307 308 static int cytherm_probe(struct usb_interface *interface, 309 const struct usb_device_id *id) 310 { 311 struct usb_device *udev = interface_to_usbdev(interface); 312 struct usb_cytherm *dev = NULL; 313 int retval = -ENOMEM; 314 315 dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL); 316 if (!dev) 317 goto error_mem; 318 319 dev->udev = usb_get_dev(udev); 320 321 usb_set_intfdata (interface, dev); 322 323 dev->brightness = 0xFF; 324 325 retval = device_create_file(&interface->dev, &dev_attr_brightness); 326 if (retval) 327 goto error; 328 retval = device_create_file(&interface->dev, &dev_attr_temp); 329 if (retval) 330 goto error; 331 retval = device_create_file(&interface->dev, &dev_attr_button); 332 if (retval) 333 goto error; 334 retval = device_create_file(&interface->dev, &dev_attr_port0); 335 if (retval) 336 goto error; 337 retval = device_create_file(&interface->dev, &dev_attr_port1); 338 if (retval) 339 goto error; 340 341 dev_info (&interface->dev, 342 "Cypress thermometer device now attached\n"); 343 return 0; 344 error: 345 device_remove_file(&interface->dev, &dev_attr_brightness); 346 device_remove_file(&interface->dev, &dev_attr_temp); 347 device_remove_file(&interface->dev, &dev_attr_button); 348 device_remove_file(&interface->dev, &dev_attr_port0); 349 device_remove_file(&interface->dev, &dev_attr_port1); 350 usb_set_intfdata (interface, NULL); 351 usb_put_dev(dev->udev); 352 kfree(dev); 353 error_mem: 354 return retval; 355 } 356 357 static void cytherm_disconnect(struct usb_interface *interface) 358 { 359 struct usb_cytherm *dev; 360 361 dev = usb_get_intfdata (interface); 362 363 device_remove_file(&interface->dev, &dev_attr_brightness); 364 device_remove_file(&interface->dev, &dev_attr_temp); 365 device_remove_file(&interface->dev, &dev_attr_button); 366 device_remove_file(&interface->dev, &dev_attr_port0); 367 device_remove_file(&interface->dev, &dev_attr_port1); 368 369 /* first remove the files, then NULL the pointer */ 370 usb_set_intfdata (interface, NULL); 371 372 usb_put_dev(dev->udev); 373 374 kfree(dev); 375 376 dev_info(&interface->dev, "Cypress thermometer now disconnected\n"); 377 } 378 379 module_usb_driver(cytherm_driver); 380 381 MODULE_AUTHOR(DRIVER_AUTHOR); 382 MODULE_DESCRIPTION(DRIVER_DESC); 383 MODULE_LICENSE("GPL"); 384