162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/*************************************************************************** 362306a36Sopenharmony_ci * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * 462306a36Sopenharmony_ci * * 562306a36Sopenharmony_ci * Based on Logitech G13 driver (v0.4) * 662306a36Sopenharmony_ci * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * 762306a36Sopenharmony_ci * * 862306a36Sopenharmony_ci ***************************************************************************/ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/hid.h> 1162306a36Sopenharmony_ci#include <linux/hid-debug.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/fb.h> 1462306a36Sopenharmony_ci#include <linux/seq_file.h> 1562306a36Sopenharmony_ci#include <linux/debugfs.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/uaccess.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "hid-picolcd.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int picolcd_debug_reset_show(struct seq_file *f, void *p) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci if (picolcd_fbinfo((struct picolcd_data *)f->private)) 2662306a36Sopenharmony_ci seq_printf(f, "all fb\n"); 2762306a36Sopenharmony_ci else 2862306a36Sopenharmony_ci seq_printf(f, "all\n"); 2962306a36Sopenharmony_ci return 0; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int picolcd_debug_reset_open(struct inode *inode, struct file *f) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci return single_open(f, picolcd_debug_reset_show, inode->i_private); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf, 3862306a36Sopenharmony_ci size_t count, loff_t *ppos) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct picolcd_data *data = ((struct seq_file *)f->private_data)->private; 4162306a36Sopenharmony_ci char buf[32]; 4262306a36Sopenharmony_ci size_t cnt = min(count, sizeof(buf)-1); 4362306a36Sopenharmony_ci if (copy_from_user(buf, user_buf, cnt)) 4462306a36Sopenharmony_ci return -EFAULT; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n')) 4762306a36Sopenharmony_ci cnt--; 4862306a36Sopenharmony_ci buf[cnt] = '\0'; 4962306a36Sopenharmony_ci if (strcmp(buf, "all") == 0) { 5062306a36Sopenharmony_ci picolcd_reset(data->hdev); 5162306a36Sopenharmony_ci picolcd_fb_reset(data, 1); 5262306a36Sopenharmony_ci } else if (strcmp(buf, "fb") == 0) { 5362306a36Sopenharmony_ci picolcd_fb_reset(data, 1); 5462306a36Sopenharmony_ci } else { 5562306a36Sopenharmony_ci return -EINVAL; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci return count; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const struct file_operations picolcd_debug_reset_fops = { 6162306a36Sopenharmony_ci .owner = THIS_MODULE, 6262306a36Sopenharmony_ci .open = picolcd_debug_reset_open, 6362306a36Sopenharmony_ci .read = seq_read, 6462306a36Sopenharmony_ci .llseek = seq_lseek, 6562306a36Sopenharmony_ci .write = picolcd_debug_reset_write, 6662306a36Sopenharmony_ci .release = single_release, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * The "eeprom" file 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, 7362306a36Sopenharmony_ci size_t s, loff_t *off) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct picolcd_data *data = f->private_data; 7662306a36Sopenharmony_ci struct picolcd_pending *resp; 7762306a36Sopenharmony_ci u8 raw_data[3]; 7862306a36Sopenharmony_ci ssize_t ret = -EIO; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (s == 0) 8162306a36Sopenharmony_ci return -EINVAL; 8262306a36Sopenharmony_ci if (*off > 0x0ff) 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* prepare buffer with info about what we want to read (addr & len) */ 8662306a36Sopenharmony_ci raw_data[0] = *off & 0xff; 8762306a36Sopenharmony_ci raw_data[1] = (*off >> 8) & 0xff; 8862306a36Sopenharmony_ci raw_data[2] = s < 20 ? s : 20; 8962306a36Sopenharmony_ci if (*off + raw_data[2] > 0xff) 9062306a36Sopenharmony_ci raw_data[2] = 0x100 - *off; 9162306a36Sopenharmony_ci resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data, 9262306a36Sopenharmony_ci sizeof(raw_data)); 9362306a36Sopenharmony_ci if (!resp) 9462306a36Sopenharmony_ci return -EIO; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { 9762306a36Sopenharmony_ci /* successful read :) */ 9862306a36Sopenharmony_ci ret = resp->raw_data[2]; 9962306a36Sopenharmony_ci if (ret > s) 10062306a36Sopenharmony_ci ret = s; 10162306a36Sopenharmony_ci if (copy_to_user(u, resp->raw_data+3, ret)) 10262306a36Sopenharmony_ci ret = -EFAULT; 10362306a36Sopenharmony_ci else 10462306a36Sopenharmony_ci *off += ret; 10562306a36Sopenharmony_ci } /* anything else is some kind of IO error */ 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci kfree(resp); 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u, 11262306a36Sopenharmony_ci size_t s, loff_t *off) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct picolcd_data *data = f->private_data; 11562306a36Sopenharmony_ci struct picolcd_pending *resp; 11662306a36Sopenharmony_ci ssize_t ret = -EIO; 11762306a36Sopenharmony_ci u8 raw_data[23]; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (s == 0) 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci if (*off > 0x0ff) 12262306a36Sopenharmony_ci return -ENOSPC; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci memset(raw_data, 0, sizeof(raw_data)); 12562306a36Sopenharmony_ci raw_data[0] = *off & 0xff; 12662306a36Sopenharmony_ci raw_data[1] = (*off >> 8) & 0xff; 12762306a36Sopenharmony_ci raw_data[2] = min_t(size_t, 20, s); 12862306a36Sopenharmony_ci if (*off + raw_data[2] > 0xff) 12962306a36Sopenharmony_ci raw_data[2] = 0x100 - *off; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2]))) 13262306a36Sopenharmony_ci return -EFAULT; 13362306a36Sopenharmony_ci resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data, 13462306a36Sopenharmony_ci sizeof(raw_data)); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!resp) 13762306a36Sopenharmony_ci return -EIO; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { 14062306a36Sopenharmony_ci /* check if written data matches */ 14162306a36Sopenharmony_ci if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) { 14262306a36Sopenharmony_ci *off += raw_data[2]; 14362306a36Sopenharmony_ci ret = raw_data[2]; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci kfree(resp); 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * Notes: 15262306a36Sopenharmony_ci * - read/write happens in chunks of at most 20 bytes, it's up to userspace 15362306a36Sopenharmony_ci * to loop in order to get more data. 15462306a36Sopenharmony_ci * - on write errors on otherwise correct write request the bytes 15562306a36Sopenharmony_ci * that should have been written are in undefined state. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic const struct file_operations picolcd_debug_eeprom_fops = { 15862306a36Sopenharmony_ci .owner = THIS_MODULE, 15962306a36Sopenharmony_ci .open = simple_open, 16062306a36Sopenharmony_ci .read = picolcd_debug_eeprom_read, 16162306a36Sopenharmony_ci .write = picolcd_debug_eeprom_write, 16262306a36Sopenharmony_ci .llseek = generic_file_llseek, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * The "flash" file 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci/* record a flash address to buf (bounds check to be done by caller) */ 16962306a36Sopenharmony_cistatic int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci buf[0] = off & 0xff; 17262306a36Sopenharmony_ci buf[1] = (off >> 8) & 0xff; 17362306a36Sopenharmony_ci if (data->addr_sz == 3) 17462306a36Sopenharmony_ci buf[2] = (off >> 16) & 0xff; 17562306a36Sopenharmony_ci return data->addr_sz == 2 ? 2 : 3; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* read a given size of data (bounds check to be done by caller) */ 17962306a36Sopenharmony_cistatic ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id, 18062306a36Sopenharmony_ci char __user *u, size_t s, loff_t *off) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct picolcd_pending *resp; 18362306a36Sopenharmony_ci u8 raw_data[4]; 18462306a36Sopenharmony_ci ssize_t ret = 0; 18562306a36Sopenharmony_ci int len_off, err = -EIO; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci while (s > 0) { 18862306a36Sopenharmony_ci err = -EIO; 18962306a36Sopenharmony_ci len_off = _picolcd_flash_setaddr(data, raw_data, *off); 19062306a36Sopenharmony_ci raw_data[len_off] = s > 32 ? 32 : s; 19162306a36Sopenharmony_ci resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1); 19262306a36Sopenharmony_ci if (!resp || !resp->in_report) 19362306a36Sopenharmony_ci goto skip; 19462306a36Sopenharmony_ci if (resp->in_report->id == REPORT_MEMORY || 19562306a36Sopenharmony_ci resp->in_report->id == REPORT_BL_READ_MEMORY) { 19662306a36Sopenharmony_ci if (memcmp(raw_data, resp->raw_data, len_off+1) != 0) 19762306a36Sopenharmony_ci goto skip; 19862306a36Sopenharmony_ci if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) { 19962306a36Sopenharmony_ci err = -EFAULT; 20062306a36Sopenharmony_ci goto skip; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci *off += raw_data[len_off]; 20362306a36Sopenharmony_ci s -= raw_data[len_off]; 20462306a36Sopenharmony_ci ret += raw_data[len_off]; 20562306a36Sopenharmony_ci err = 0; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ciskip: 20862306a36Sopenharmony_ci kfree(resp); 20962306a36Sopenharmony_ci if (err) 21062306a36Sopenharmony_ci return ret > 0 ? ret : err; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci return ret; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic ssize_t picolcd_debug_flash_read(struct file *f, char __user *u, 21662306a36Sopenharmony_ci size_t s, loff_t *off) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct picolcd_data *data = f->private_data; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (s == 0) 22162306a36Sopenharmony_ci return -EINVAL; 22262306a36Sopenharmony_ci if (*off > 0x05fff) 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci if (*off + s > 0x05fff) 22562306a36Sopenharmony_ci s = 0x06000 - *off; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (data->status & PICOLCD_BOOTLOADER) 22862306a36Sopenharmony_ci return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off); 22962306a36Sopenharmony_ci else 23062306a36Sopenharmony_ci return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* erase block aligned to 64bytes boundary */ 23462306a36Sopenharmony_cistatic ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id, 23562306a36Sopenharmony_ci loff_t *off) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct picolcd_pending *resp; 23862306a36Sopenharmony_ci u8 raw_data[3]; 23962306a36Sopenharmony_ci int len_off; 24062306a36Sopenharmony_ci ssize_t ret = -EIO; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (*off & 0x3f) 24362306a36Sopenharmony_ci return -EINVAL; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci len_off = _picolcd_flash_setaddr(data, raw_data, *off); 24662306a36Sopenharmony_ci resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off); 24762306a36Sopenharmony_ci if (!resp || !resp->in_report) 24862306a36Sopenharmony_ci goto skip; 24962306a36Sopenharmony_ci if (resp->in_report->id == REPORT_MEMORY || 25062306a36Sopenharmony_ci resp->in_report->id == REPORT_BL_ERASE_MEMORY) { 25162306a36Sopenharmony_ci if (memcmp(raw_data, resp->raw_data, len_off) != 0) 25262306a36Sopenharmony_ci goto skip; 25362306a36Sopenharmony_ci ret = 0; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ciskip: 25662306a36Sopenharmony_ci kfree(resp); 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* write a given size of data (bounds check to be done by caller) */ 26162306a36Sopenharmony_cistatic ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id, 26262306a36Sopenharmony_ci const char __user *u, size_t s, loff_t *off) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct picolcd_pending *resp; 26562306a36Sopenharmony_ci u8 raw_data[36]; 26662306a36Sopenharmony_ci ssize_t ret = 0; 26762306a36Sopenharmony_ci int len_off, err = -EIO; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci while (s > 0) { 27062306a36Sopenharmony_ci err = -EIO; 27162306a36Sopenharmony_ci len_off = _picolcd_flash_setaddr(data, raw_data, *off); 27262306a36Sopenharmony_ci raw_data[len_off] = s > 32 ? 32 : s; 27362306a36Sopenharmony_ci if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) { 27462306a36Sopenharmony_ci err = -EFAULT; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, 27862306a36Sopenharmony_ci len_off+1+raw_data[len_off]); 27962306a36Sopenharmony_ci if (!resp || !resp->in_report) 28062306a36Sopenharmony_ci goto skip; 28162306a36Sopenharmony_ci if (resp->in_report->id == REPORT_MEMORY || 28262306a36Sopenharmony_ci resp->in_report->id == REPORT_BL_WRITE_MEMORY) { 28362306a36Sopenharmony_ci if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0) 28462306a36Sopenharmony_ci goto skip; 28562306a36Sopenharmony_ci *off += raw_data[len_off]; 28662306a36Sopenharmony_ci s -= raw_data[len_off]; 28762306a36Sopenharmony_ci ret += raw_data[len_off]; 28862306a36Sopenharmony_ci err = 0; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ciskip: 29162306a36Sopenharmony_ci kfree(resp); 29262306a36Sopenharmony_ci if (err) 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci return ret > 0 ? ret : err; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u, 29962306a36Sopenharmony_ci size_t s, loff_t *off) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct picolcd_data *data = f->private_data; 30262306a36Sopenharmony_ci ssize_t err, ret = 0; 30362306a36Sopenharmony_ci int report_erase, report_write; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (s == 0) 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci if (*off > 0x5fff) 30862306a36Sopenharmony_ci return -ENOSPC; 30962306a36Sopenharmony_ci if (s & 0x3f) 31062306a36Sopenharmony_ci return -EINVAL; 31162306a36Sopenharmony_ci if (*off & 0x3f) 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (data->status & PICOLCD_BOOTLOADER) { 31562306a36Sopenharmony_ci report_erase = REPORT_BL_ERASE_MEMORY; 31662306a36Sopenharmony_ci report_write = REPORT_BL_WRITE_MEMORY; 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci report_erase = REPORT_ERASE_MEMORY; 31962306a36Sopenharmony_ci report_write = REPORT_WRITE_MEMORY; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci mutex_lock(&data->mutex_flash); 32262306a36Sopenharmony_ci while (s > 0) { 32362306a36Sopenharmony_ci err = _picolcd_flash_erase64(data, report_erase, off); 32462306a36Sopenharmony_ci if (err) 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci err = _picolcd_flash_write(data, report_write, u, 64, off); 32762306a36Sopenharmony_ci if (err < 0) 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci ret += err; 33062306a36Sopenharmony_ci *off += err; 33162306a36Sopenharmony_ci s -= err; 33262306a36Sopenharmony_ci if (err != 64) 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci mutex_unlock(&data->mutex_flash); 33662306a36Sopenharmony_ci return ret > 0 ? ret : err; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* 34062306a36Sopenharmony_ci * Notes: 34162306a36Sopenharmony_ci * - concurrent writing is prevented by mutex and all writes must be 34262306a36Sopenharmony_ci * n*64 bytes and 64-byte aligned, each write being preceded by an 34362306a36Sopenharmony_ci * ERASE which erases a 64byte block. 34462306a36Sopenharmony_ci * If less than requested was written or an error is returned for an 34562306a36Sopenharmony_ci * otherwise correct write request the next 64-byte block which should 34662306a36Sopenharmony_ci * have been written is in undefined state (mostly: original, erased, 34762306a36Sopenharmony_ci * (half-)written with write error) 34862306a36Sopenharmony_ci * - reading can happen without special restriction 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_cistatic const struct file_operations picolcd_debug_flash_fops = { 35162306a36Sopenharmony_ci .owner = THIS_MODULE, 35262306a36Sopenharmony_ci .open = simple_open, 35362306a36Sopenharmony_ci .read = picolcd_debug_flash_read, 35462306a36Sopenharmony_ci .write = picolcd_debug_flash_write, 35562306a36Sopenharmony_ci .llseek = generic_file_llseek, 35662306a36Sopenharmony_ci}; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * Helper code for HID report level dumping/debugging 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_cistatic const char * const error_codes[] = { 36362306a36Sopenharmony_ci "success", "parameter missing", "data_missing", "block readonly", 36462306a36Sopenharmony_ci "block not erasable", "block too big", "section overflow", 36562306a36Sopenharmony_ci "invalid command length", "invalid data length", 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, 36962306a36Sopenharmony_ci const size_t data_len) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int i, j; 37262306a36Sopenharmony_ci for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) { 37362306a36Sopenharmony_ci dst[j++] = hex_asc[(data[i] >> 4) & 0x0f]; 37462306a36Sopenharmony_ci dst[j++] = hex_asc[data[i] & 0x0f]; 37562306a36Sopenharmony_ci dst[j++] = ' '; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci dst[j] = '\0'; 37862306a36Sopenharmony_ci if (j > 0) 37962306a36Sopenharmony_ci dst[j-1] = '\n'; 38062306a36Sopenharmony_ci if (i < data_len && j > 2) 38162306a36Sopenharmony_ci dst[j-2] = dst[j-3] = '.'; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_civoid picolcd_debug_out_report(struct picolcd_data *data, 38562306a36Sopenharmony_ci struct hid_device *hdev, struct hid_report *report) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci u8 *raw_data; 38862306a36Sopenharmony_ci int raw_size = (report->size >> 3) + 1; 38962306a36Sopenharmony_ci char *buff; 39062306a36Sopenharmony_ci#define BUFF_SZ 256 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Avoid unnecessary overhead if debugfs is disabled */ 39362306a36Sopenharmony_ci if (list_empty(&hdev->debug_list)) 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci buff = kmalloc(BUFF_SZ, GFP_ATOMIC); 39762306a36Sopenharmony_ci if (!buff) 39862306a36Sopenharmony_ci return; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci raw_data = hid_alloc_report_buf(report, GFP_ATOMIC); 40162306a36Sopenharmony_ci if (!raw_data) { 40262306a36Sopenharmony_ci kfree(buff); 40362306a36Sopenharmony_ci return; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", 40762306a36Sopenharmony_ci report->id, raw_size); 40862306a36Sopenharmony_ci hid_debug_event(hdev, buff); 40962306a36Sopenharmony_ci raw_data[0] = report->id; 41062306a36Sopenharmony_ci hid_output_report(report, raw_data); 41162306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); 41262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci switch (report->id) { 41562306a36Sopenharmony_ci case REPORT_LED_STATE: 41662306a36Sopenharmony_ci /* 1 data byte with GPO state */ 41762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 41862306a36Sopenharmony_ci "REPORT_LED_STATE", report->id, raw_size-1); 41962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 42062306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]); 42162306a36Sopenharmony_ci hid_debug_event(hdev, buff); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci case REPORT_BRIGHTNESS: 42462306a36Sopenharmony_ci /* 1 data byte with brightness */ 42562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 42662306a36Sopenharmony_ci "REPORT_BRIGHTNESS", report->id, raw_size-1); 42762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 42862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]); 42962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci case REPORT_CONTRAST: 43262306a36Sopenharmony_ci /* 1 data byte with contrast */ 43362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 43462306a36Sopenharmony_ci "REPORT_CONTRAST", report->id, raw_size-1); 43562306a36Sopenharmony_ci hid_debug_event(hdev, buff); 43662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]); 43762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci case REPORT_RESET: 44062306a36Sopenharmony_ci /* 2 data bytes with reset duration in ms */ 44162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 44262306a36Sopenharmony_ci "REPORT_RESET", report->id, raw_size-1); 44362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 44462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n", 44562306a36Sopenharmony_ci raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]); 44662306a36Sopenharmony_ci hid_debug_event(hdev, buff); 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case REPORT_LCD_CMD: 44962306a36Sopenharmony_ci /* 63 data bytes with LCD commands */ 45062306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 45162306a36Sopenharmony_ci "REPORT_LCD_CMD", report->id, raw_size-1); 45262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 45362306a36Sopenharmony_ci /* TODO: format decoding */ 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci case REPORT_LCD_DATA: 45662306a36Sopenharmony_ci /* 63 data bytes with LCD data */ 45762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 45862306a36Sopenharmony_ci "REPORT_LCD_CMD", report->id, raw_size-1); 45962306a36Sopenharmony_ci /* TODO: format decoding */ 46062306a36Sopenharmony_ci hid_debug_event(hdev, buff); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case REPORT_LCD_CMD_DATA: 46362306a36Sopenharmony_ci /* 63 data bytes with LCD commands and data */ 46462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 46562306a36Sopenharmony_ci "REPORT_LCD_CMD", report->id, raw_size-1); 46662306a36Sopenharmony_ci /* TODO: format decoding */ 46762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci case REPORT_EE_READ: 47062306a36Sopenharmony_ci /* 3 data bytes with read area description */ 47162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 47262306a36Sopenharmony_ci "REPORT_EE_READ", report->id, raw_size-1); 47362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 47462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", 47562306a36Sopenharmony_ci raw_data[2], raw_data[1]); 47662306a36Sopenharmony_ci hid_debug_event(hdev, buff); 47762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); 47862306a36Sopenharmony_ci hid_debug_event(hdev, buff); 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci case REPORT_EE_WRITE: 48162306a36Sopenharmony_ci /* 3+1..20 data bytes with write area description */ 48262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 48362306a36Sopenharmony_ci "REPORT_EE_WRITE", report->id, raw_size-1); 48462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 48562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", 48662306a36Sopenharmony_ci raw_data[2], raw_data[1]); 48762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 48862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); 48962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 49062306a36Sopenharmony_ci if (raw_data[3] == 0) { 49162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNo data\n"); 49262306a36Sopenharmony_ci } else if (raw_data[3] + 4 <= raw_size) { 49362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData: "); 49462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 49562306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); 49662306a36Sopenharmony_ci } else { 49762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData overflowed\n"); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci case REPORT_ERASE_MEMORY: 50262306a36Sopenharmony_ci case REPORT_BL_ERASE_MEMORY: 50362306a36Sopenharmony_ci /* 3 data bytes with pointer inside erase block */ 50462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 50562306a36Sopenharmony_ci "REPORT_ERASE_MEMORY", report->id, raw_size-1); 50662306a36Sopenharmony_ci hid_debug_event(hdev, buff); 50762306a36Sopenharmony_ci switch (data->addr_sz) { 50862306a36Sopenharmony_ci case 2: 50962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n", 51062306a36Sopenharmony_ci raw_data[2], raw_data[1]); 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci case 3: 51362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n", 51462306a36Sopenharmony_ci raw_data[3], raw_data[2], raw_data[1]); 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci default: 51762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNot supported\n"); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case REPORT_READ_MEMORY: 52262306a36Sopenharmony_ci case REPORT_BL_READ_MEMORY: 52362306a36Sopenharmony_ci /* 4 data bytes with read area description */ 52462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 52562306a36Sopenharmony_ci "REPORT_READ_MEMORY", report->id, raw_size-1); 52662306a36Sopenharmony_ci hid_debug_event(hdev, buff); 52762306a36Sopenharmony_ci switch (data->addr_sz) { 52862306a36Sopenharmony_ci case 2: 52962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", 53062306a36Sopenharmony_ci raw_data[2], raw_data[1]); 53162306a36Sopenharmony_ci hid_debug_event(hdev, buff); 53262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci case 3: 53562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", 53662306a36Sopenharmony_ci raw_data[3], raw_data[2], raw_data[1]); 53762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 53862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci default: 54162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNot supported\n"); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci case REPORT_WRITE_MEMORY: 54662306a36Sopenharmony_ci case REPORT_BL_WRITE_MEMORY: 54762306a36Sopenharmony_ci /* 4+1..32 data bytes with write adrea description */ 54862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 54962306a36Sopenharmony_ci "REPORT_WRITE_MEMORY", report->id, raw_size-1); 55062306a36Sopenharmony_ci hid_debug_event(hdev, buff); 55162306a36Sopenharmony_ci switch (data->addr_sz) { 55262306a36Sopenharmony_ci case 2: 55362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", 55462306a36Sopenharmony_ci raw_data[2], raw_data[1]); 55562306a36Sopenharmony_ci hid_debug_event(hdev, buff); 55662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); 55762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 55862306a36Sopenharmony_ci if (raw_data[3] == 0) { 55962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNo data\n"); 56062306a36Sopenharmony_ci } else if (raw_data[3] + 4 <= raw_size) { 56162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData: "); 56262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 56362306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); 56462306a36Sopenharmony_ci } else { 56562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData overflowed\n"); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci case 3: 56962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", 57062306a36Sopenharmony_ci raw_data[3], raw_data[2], raw_data[1]); 57162306a36Sopenharmony_ci hid_debug_event(hdev, buff); 57262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); 57362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 57462306a36Sopenharmony_ci if (raw_data[4] == 0) { 57562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNo data\n"); 57662306a36Sopenharmony_ci } else if (raw_data[4] + 5 <= raw_size) { 57762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData: "); 57862306a36Sopenharmony_ci hid_debug_event(hdev, buff); 57962306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData overflowed\n"); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci default: 58562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNot supported\n"); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci case REPORT_SPLASH_RESTART: 59062306a36Sopenharmony_ci /* TODO */ 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci case REPORT_EXIT_KEYBOARD: 59362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 59462306a36Sopenharmony_ci "REPORT_EXIT_KEYBOARD", report->id, raw_size-1); 59562306a36Sopenharmony_ci hid_debug_event(hdev, buff); 59662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", 59762306a36Sopenharmony_ci raw_data[1] | (raw_data[2] << 8), 59862306a36Sopenharmony_ci raw_data[2], raw_data[1]); 59962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci case REPORT_VERSION: 60262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 60362306a36Sopenharmony_ci "REPORT_VERSION", report->id, raw_size-1); 60462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci case REPORT_DEVID: 60762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 60862306a36Sopenharmony_ci "REPORT_DEVID", report->id, raw_size-1); 60962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case REPORT_SPLASH_SIZE: 61262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 61362306a36Sopenharmony_ci "REPORT_SPLASH_SIZE", report->id, raw_size-1); 61462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci case REPORT_HOOK_VERSION: 61762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 61862306a36Sopenharmony_ci "REPORT_HOOK_VERSION", report->id, raw_size-1); 61962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case REPORT_EXIT_FLASHER: 62262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 62362306a36Sopenharmony_ci "REPORT_VERSION", report->id, raw_size-1); 62462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 62562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", 62662306a36Sopenharmony_ci raw_data[1] | (raw_data[2] << 8), 62762306a36Sopenharmony_ci raw_data[2], raw_data[1]); 62862306a36Sopenharmony_ci hid_debug_event(hdev, buff); 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci default: 63162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", 63262306a36Sopenharmony_ci "<unknown>", report->id, raw_size-1); 63362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci wake_up_interruptible(&hdev->debug_wait); 63762306a36Sopenharmony_ci kfree(raw_data); 63862306a36Sopenharmony_ci kfree(buff); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_civoid picolcd_debug_raw_event(struct picolcd_data *data, 64262306a36Sopenharmony_ci struct hid_device *hdev, struct hid_report *report, 64362306a36Sopenharmony_ci u8 *raw_data, int size) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci char *buff; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci#define BUFF_SZ 256 64862306a36Sopenharmony_ci /* Avoid unnecessary overhead if debugfs is disabled */ 64962306a36Sopenharmony_ci if (list_empty(&hdev->debug_list)) 65062306a36Sopenharmony_ci return; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci buff = kmalloc(BUFF_SZ, GFP_ATOMIC); 65362306a36Sopenharmony_ci if (!buff) 65462306a36Sopenharmony_ci return; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci switch (report->id) { 65762306a36Sopenharmony_ci case REPORT_ERROR_CODE: 65862306a36Sopenharmony_ci /* 2 data bytes with affected report and error code */ 65962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 66062306a36Sopenharmony_ci "REPORT_ERROR_CODE", report->id, size-1); 66162306a36Sopenharmony_ci hid_debug_event(hdev, buff); 66262306a36Sopenharmony_ci if (raw_data[2] < ARRAY_SIZE(error_codes)) 66362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n", 66462306a36Sopenharmony_ci raw_data[2], error_codes[raw_data[2]], raw_data[1]); 66562306a36Sopenharmony_ci else 66662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n", 66762306a36Sopenharmony_ci raw_data[2], raw_data[1]); 66862306a36Sopenharmony_ci hid_debug_event(hdev, buff); 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case REPORT_KEY_STATE: 67162306a36Sopenharmony_ci /* 2 data bytes with key state */ 67262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 67362306a36Sopenharmony_ci "REPORT_KEY_STATE", report->id, size-1); 67462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 67562306a36Sopenharmony_ci if (raw_data[1] == 0) 67662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNo key pressed\n"); 67762306a36Sopenharmony_ci else if (raw_data[2] == 0) 67862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n", 67962306a36Sopenharmony_ci raw_data[1], raw_data[1]); 68062306a36Sopenharmony_ci else 68162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n", 68262306a36Sopenharmony_ci raw_data[1], raw_data[1], raw_data[2], raw_data[2]); 68362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci case REPORT_IR_DATA: 68662306a36Sopenharmony_ci /* Up to 20 byes of IR scancode data */ 68762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 68862306a36Sopenharmony_ci "REPORT_IR_DATA", report->id, size-1); 68962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 69062306a36Sopenharmony_ci if (raw_data[1] == 0) { 69162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n"); 69262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 69362306a36Sopenharmony_ci } else if (raw_data[1] + 1 <= size) { 69462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ", 69562306a36Sopenharmony_ci raw_data[1]); 69662306a36Sopenharmony_ci hid_debug_event(hdev, buff); 69762306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]); 69862306a36Sopenharmony_ci hid_debug_event(hdev, buff); 69962306a36Sopenharmony_ci } else { 70062306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n", 70162306a36Sopenharmony_ci raw_data[1]-1); 70262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci case REPORT_EE_DATA: 70662306a36Sopenharmony_ci /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */ 70762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 70862306a36Sopenharmony_ci "REPORT_EE_DATA", report->id, size-1); 70962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 71062306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", 71162306a36Sopenharmony_ci raw_data[2], raw_data[1]); 71262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 71362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); 71462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 71562306a36Sopenharmony_ci if (raw_data[3] == 0) { 71662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNo data\n"); 71762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 71862306a36Sopenharmony_ci } else if (raw_data[3] + 4 <= size) { 71962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData: "); 72062306a36Sopenharmony_ci hid_debug_event(hdev, buff); 72162306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); 72262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 72362306a36Sopenharmony_ci } else { 72462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData overflowed\n"); 72562306a36Sopenharmony_ci hid_debug_event(hdev, buff); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci case REPORT_MEMORY: 72962306a36Sopenharmony_ci /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRITE_MEMORY */ 73062306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 73162306a36Sopenharmony_ci "REPORT_MEMORY", report->id, size-1); 73262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 73362306a36Sopenharmony_ci switch (data->addr_sz) { 73462306a36Sopenharmony_ci case 2: 73562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", 73662306a36Sopenharmony_ci raw_data[2], raw_data[1]); 73762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 73862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); 73962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 74062306a36Sopenharmony_ci if (raw_data[3] == 0) { 74162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNo data\n"); 74262306a36Sopenharmony_ci } else if (raw_data[3] + 4 <= size) { 74362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData: "); 74462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 74562306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); 74662306a36Sopenharmony_ci } else { 74762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData overflowed\n"); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci case 3: 75162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", 75262306a36Sopenharmony_ci raw_data[3], raw_data[2], raw_data[1]); 75362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 75462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); 75562306a36Sopenharmony_ci hid_debug_event(hdev, buff); 75662306a36Sopenharmony_ci if (raw_data[4] == 0) { 75762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNo data\n"); 75862306a36Sopenharmony_ci } else if (raw_data[4] + 5 <= size) { 75962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData: "); 76062306a36Sopenharmony_ci hid_debug_event(hdev, buff); 76162306a36Sopenharmony_ci dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); 76262306a36Sopenharmony_ci } else { 76362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tData overflowed\n"); 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci default: 76762306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tNot supported\n"); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci hid_debug_event(hdev, buff); 77062306a36Sopenharmony_ci break; 77162306a36Sopenharmony_ci case REPORT_VERSION: 77262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 77362306a36Sopenharmony_ci "REPORT_VERSION", report->id, size-1); 77462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 77562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", 77662306a36Sopenharmony_ci raw_data[2], raw_data[1]); 77762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci case REPORT_BL_ERASE_MEMORY: 78062306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 78162306a36Sopenharmony_ci "REPORT_BL_ERASE_MEMORY", report->id, size-1); 78262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 78362306a36Sopenharmony_ci /* TODO */ 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci case REPORT_BL_READ_MEMORY: 78662306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 78762306a36Sopenharmony_ci "REPORT_BL_READ_MEMORY", report->id, size-1); 78862306a36Sopenharmony_ci hid_debug_event(hdev, buff); 78962306a36Sopenharmony_ci /* TODO */ 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci case REPORT_BL_WRITE_MEMORY: 79262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 79362306a36Sopenharmony_ci "REPORT_BL_WRITE_MEMORY", report->id, size-1); 79462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 79562306a36Sopenharmony_ci /* TODO */ 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case REPORT_DEVID: 79862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 79962306a36Sopenharmony_ci "REPORT_DEVID", report->id, size-1); 80062306a36Sopenharmony_ci hid_debug_event(hdev, buff); 80162306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n", 80262306a36Sopenharmony_ci raw_data[1], raw_data[2], raw_data[3], raw_data[4]); 80362306a36Sopenharmony_ci hid_debug_event(hdev, buff); 80462306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n", 80562306a36Sopenharmony_ci raw_data[5]); 80662306a36Sopenharmony_ci hid_debug_event(hdev, buff); 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci case REPORT_SPLASH_SIZE: 80962306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 81062306a36Sopenharmony_ci "REPORT_SPLASH_SIZE", report->id, size-1); 81162306a36Sopenharmony_ci hid_debug_event(hdev, buff); 81262306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n", 81362306a36Sopenharmony_ci (raw_data[2] << 8) | raw_data[1]); 81462306a36Sopenharmony_ci hid_debug_event(hdev, buff); 81562306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n", 81662306a36Sopenharmony_ci (raw_data[4] << 8) | raw_data[3]); 81762306a36Sopenharmony_ci hid_debug_event(hdev, buff); 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci case REPORT_HOOK_VERSION: 82062306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 82162306a36Sopenharmony_ci "REPORT_HOOK_VERSION", report->id, size-1); 82262306a36Sopenharmony_ci hid_debug_event(hdev, buff); 82362306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", 82462306a36Sopenharmony_ci raw_data[1], raw_data[2]); 82562306a36Sopenharmony_ci hid_debug_event(hdev, buff); 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci default: 82862306a36Sopenharmony_ci snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", 82962306a36Sopenharmony_ci "<unknown>", report->id, size-1); 83062306a36Sopenharmony_ci hid_debug_event(hdev, buff); 83162306a36Sopenharmony_ci break; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci wake_up_interruptible(&hdev->debug_wait); 83462306a36Sopenharmony_ci kfree(buff); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_civoid picolcd_init_devfs(struct picolcd_data *data, 83862306a36Sopenharmony_ci struct hid_report *eeprom_r, struct hid_report *eeprom_w, 83962306a36Sopenharmony_ci struct hid_report *flash_r, struct hid_report *flash_w, 84062306a36Sopenharmony_ci struct hid_report *reset) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct hid_device *hdev = data->hdev; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci mutex_init(&data->mutex_flash); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* reset */ 84762306a36Sopenharmony_ci if (reset) 84862306a36Sopenharmony_ci data->debug_reset = debugfs_create_file("reset", 0600, 84962306a36Sopenharmony_ci hdev->debug_dir, data, &picolcd_debug_reset_fops); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* eeprom */ 85262306a36Sopenharmony_ci if (eeprom_r || eeprom_w) 85362306a36Sopenharmony_ci data->debug_eeprom = debugfs_create_file("eeprom", 85462306a36Sopenharmony_ci (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0), 85562306a36Sopenharmony_ci hdev->debug_dir, data, &picolcd_debug_eeprom_fops); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* flash */ 85862306a36Sopenharmony_ci if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8) 85962306a36Sopenharmony_ci data->addr_sz = flash_r->field[0]->report_count - 1; 86062306a36Sopenharmony_ci else 86162306a36Sopenharmony_ci data->addr_sz = -1; 86262306a36Sopenharmony_ci if (data->addr_sz == 2 || data->addr_sz == 3) { 86362306a36Sopenharmony_ci data->debug_flash = debugfs_create_file("flash", 86462306a36Sopenharmony_ci (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0), 86562306a36Sopenharmony_ci hdev->debug_dir, data, &picolcd_debug_flash_fops); 86662306a36Sopenharmony_ci } else if (flash_r || flash_w) 86762306a36Sopenharmony_ci hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n"); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_civoid picolcd_exit_devfs(struct picolcd_data *data) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct dentry *dent; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci dent = data->debug_reset; 87562306a36Sopenharmony_ci data->debug_reset = NULL; 87662306a36Sopenharmony_ci debugfs_remove(dent); 87762306a36Sopenharmony_ci dent = data->debug_eeprom; 87862306a36Sopenharmony_ci data->debug_eeprom = NULL; 87962306a36Sopenharmony_ci debugfs_remove(dent); 88062306a36Sopenharmony_ci dent = data->debug_flash; 88162306a36Sopenharmony_ci data->debug_flash = NULL; 88262306a36Sopenharmony_ci debugfs_remove(dent); 88362306a36Sopenharmony_ci mutex_destroy(&data->mutex_flash); 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 886