static void g19_remove(struct hid_device *hdev) { struct g19_data *data; int i; /* Get the internal g19 data buffer */ data = hid_get_drvdata(hdev); input_unregister_device(data->input_dev); kfree(data->name); /* Clean up the leds */ for (i = 0; i < LED_COUNT; i++) { led_classdev_unregister(data->led_cdev[i]); kfree(data->led_cdev[i]->name); kfree(data->led_cdev[i]); } gfb_remove(data->gfb_data); hdev->ll_driver->close(hdev); hid_hw_stop(hdev); sysfs_remove_group(&(hdev->dev.kobj), &g19_attr_group); usb_free_urb(data->ep1_urb); /* Finally, clean up the g19 data itself */ kfree(data); }
static void g19_remove(struct hid_device *hdev) { struct gcore_data *gdata = hid_get_drvdata(hdev); struct g19_data *g19data = gdata->data; usb_poison_urb(g19data->ep1_urb); sysfs_remove_group(&(hdev->dev.kobj), &g19_attr_group); gfb_remove(gdata->gfb_data); gcore_leds_remove(gdata); gcore_input_remove(gdata); gcore_hid_close(gdata); usb_free_urb(g19data->ep1_urb); kfree(g19data); gcore_free_data(gdata); }
static int g19_probe(struct hid_device *hdev, const struct hid_device_id *id) { int error; struct gcore_data *gdata; struct g19_data *g19data; dev_dbg(&hdev->dev, "Logitech G19 HID hardware probe..."); gdata = gcore_alloc_data(G19_NAME, hdev); if (gdata == NULL) { dev_err(&hdev->dev, G19_NAME " can't allocate space for device attributes\n"); error = -ENOMEM; goto err_no_cleanup; } g19data = kzalloc(sizeof(struct g19_data), GFP_KERNEL); if (g19data == NULL) { error = -ENOMEM; goto err_cleanup_gdata; } gdata->data = g19data; init_completion(&g19data->ready); g19data->ep1_urb = usb_alloc_urb(0, GFP_KERNEL); if (g19data->ep1_urb == NULL) { dev_err(&hdev->dev, "%s: ERROR: can't alloc ep1 urb stuff\n", gdata->name); error = -ENOMEM; goto err_cleanup_g19data; } error = gcore_hid_open(gdata); if (error) { dev_err(&hdev->dev, "%s error opening hid device\n", gdata->name); goto err_cleanup_ep1_urb; } error = gcore_input_probe(gdata, g19_default_keymap, ARRAY_SIZE(g19_default_keymap)); if (error) { dev_err(&hdev->dev, "%s error registering input device\n", gdata->name); goto err_cleanup_hid; } error = read_feature_reports(gdata); if (error) { dev_err(&hdev->dev, "%s error reading feature reports\n", gdata->name); goto err_cleanup_input; } error = gcore_leds_probe(gdata, g19_led_cdevs, ARRAY_SIZE(g19_led_cdevs)); if (error) { dev_err(&hdev->dev, "%s error registering leds\n", gdata->name); goto err_cleanup_input; } gdata->gfb_data = gfb_probe(hdev, GFB_PANEL_TYPE_320_240_16); if (gdata->gfb_data == NULL) { dev_err(&hdev->dev, "%s error registering framebuffer\n", gdata->name); goto err_cleanup_leds; } error = sysfs_create_group(&(hdev->dev.kobj), &g19_attr_group); if (error) { dev_err(&hdev->dev, "%s failed to create sysfs group attributes\n", gdata->name); goto err_cleanup_gfb; } wait_ready(gdata); /* * Clear the LEDs */ g19data->backlight_rgb[0] = G19_DEFAULT_RED; g19data->backlight_rgb[1] = G19_DEFAULT_GREEN; g19data->backlight_rgb[2] = G19_DEFAULT_BLUE; g19data->screen_bl = G19_DEFAULT_BRIGHTNESS; g19_led_bl_send(hdev); g19_led_mbtns_send(hdev); g19_led_screen_bl_send(hdev); send_finalize_report(gdata); error = g19_ep1_read(hdev); if (error) { dev_err(&hdev->dev, "%s failed to read ep1\n", gdata->name); goto err_cleanup_sysfs; } dbg_hid("G19 activated and initialized\n"); /* Everything went well */ return 0; err_cleanup_sysfs: sysfs_remove_group(&(hdev->dev.kobj), &g19_attr_group); err_cleanup_gfb: gfb_remove(gdata->gfb_data); err_cleanup_leds: gcore_leds_remove(gdata); err_cleanup_input: gcore_input_remove(gdata); err_cleanup_hid: gcore_hid_close(gdata); err_cleanup_ep1_urb: usb_free_urb(g19data->ep1_urb); err_cleanup_g19data: kfree(g19data); err_cleanup_gdata: gcore_free_data(gdata); err_no_cleanup: hid_set_drvdata(hdev, NULL); return error; }
static int g19_probe(struct hid_device *hdev, const struct hid_device_id *id) { unsigned long irq_flags; int error; struct gcommon_data *gdata; struct g19_data *g19data; int i; int led_num; struct usb_interface *intf; struct usb_device *usbdev; struct list_head *feature_report_list = &hdev->report_enum[HID_FEATURE_REPORT].report_list; struct hid_report *report; char *led_name; dev_dbg(&hdev->dev, "Logitech G19 HID hardware probe..."); /* Get the usb device to send the start report on */ intf = to_usb_interface(hdev->dev.parent); usbdev = interface_to_usbdev(intf); /* * Let's allocate the g19 data structure, set some reasonable * defaults, and associate it with the device */ gdata = kzalloc(sizeof(struct gcommon_data), GFP_KERNEL); if (gdata == NULL) { dev_err(&hdev->dev, "can't allocate space for Logitech G19 device attributes\n"); error = -ENOMEM; goto err_no_cleanup; } g19data = kzalloc(sizeof(struct g19_data), GFP_KERNEL); if (g19data == NULL) { dev_err(&hdev->dev, "can't allocate space for Logitech G19 device attributes\n"); error = -ENOMEM; goto err_cleanup_gdata; } gdata->data = g19data; spin_lock_init(&gdata->lock); init_completion(&g19data->ready); gdata->hdev = hdev; g19data->ep1_urb = usb_alloc_urb(0, GFP_KERNEL); if (g19data->ep1_urb == NULL) { dev_err(&hdev->dev, G19_NAME ": ERROR: can't alloc ep1 urb stuff\n"); error = -ENOMEM; goto err_cleanup_g19data; } hid_set_drvdata(hdev, gdata); dbg_hid("Preparing to parse " G19_NAME " hid reports\n"); /* Parse the device reports and start it up */ error = hid_parse(hdev); if (error) { dev_err(&hdev->dev, G19_NAME " device report parse failed\n"); error = -EINVAL; goto err_cleanup_ep1_urb; } error = hid_hw_start(hdev, HID_CONNECT_DEFAULT | HID_CONNECT_HIDINPUT_FORCE); if (error) { dev_err(&hdev->dev, G19_NAME " hardware start failed\n"); error = -EINVAL; goto err_cleanup_ep1_urb; } dbg_hid(G19_NAME " claimed: %d\n", hdev->claimed); error = hdev->ll_driver->open(hdev); if (error) { dev_err(&hdev->dev, G19_NAME " failed to open input interrupt pipe for key and joystick events\n"); error = -EINVAL; goto err_cleanup_hw_start; } /* Set up the input device for the key I/O */ gdata->input_dev = input_allocate_device(); if (gdata->input_dev == NULL) { dev_err(&hdev->dev, G19_NAME " error initializing the input device"); error = -ENOMEM; goto err_cleanup_hw_start; } input_set_drvdata(gdata->input_dev, gdata); gdata->input_dev->name = G19_NAME; gdata->input_dev->phys = hdev->phys; gdata->input_dev->uniq = hdev->uniq; gdata->input_dev->id.bustype = hdev->bus; gdata->input_dev->id.vendor = hdev->vendor; gdata->input_dev->id.product = hdev->product; gdata->input_dev->id.version = hdev->version; gdata->input_dev->dev.parent = hdev->dev.parent; gdata->input_dev->keycode = gdata->input_data.keycode; gdata->input_dev->keycodemax = G19_KEYMAP_SIZE; gdata->input_dev->keycodesize = sizeof(unsigned int); gdata->input_dev->setkeycode = ginput_setkeycode; gdata->input_dev->getkeycode = ginput_getkeycode; input_set_capability(gdata->input_dev, EV_KEY, KEY_UNKNOWN); gdata->input_dev->evbit[0] |= BIT_MASK(EV_REP); gdata->input_data.notify_keymap_switched = g19_notify_keymap_switched; error = ginput_alloc(gdata, G19_KEYS); if (error) { dev_err(&hdev->dev, G19_NAME " error allocating memory for the input device"); goto err_cleanup_input_dev; } g19_initialize_keymap(gdata); error = input_register_device(gdata->input_dev); if (error) { dev_err(&hdev->dev, G19_NAME " error registering the input device"); error = -EINVAL; goto err_cleanup_input_dev_data; } if (list_empty(feature_report_list)) { dev_err(&hdev->dev, "no feature report found\n"); error = -ENODEV; goto err_cleanup_input_dev_reg; } dbg_hid(G19_NAME " feature report found\n"); list_for_each_entry(report, feature_report_list, list) { switch (report->id) { case 0x04: g19data->feature_report_4 = report; break; case 0x05: g19data->led_report = report; break; case 0x06: g19data->start_input_report = report; break; case 0x07: g19data->backlight_report = report; break; default: break; } dbg_hid(G19_NAME " Feature report: id=%u type=%u size=%u maxfield=%u report_count=%u\n", report->id, report->type, report->size, report->maxfield, report->field[0]->report_count); } dbg_hid("Found all reports\n"); /* Create the LED structures */ for (i = 0; i < LED_COUNT; i++) { g19data->led_cdev[i] = kzalloc(sizeof(struct led_classdev), GFP_KERNEL); if (g19data->led_cdev[i] == NULL) { dev_err(&hdev->dev, G19_NAME " error allocating memory for led %d", i); error = -ENOMEM; goto err_cleanup_led_structs; } /* Set the accessor functions by copying from template*/ *(g19data->led_cdev[i]) = g19_led_cdevs[i]; /* * Allocate memory for the LED name * * Since led_classdev->name is a const char* we'll use an * intermediate until the name is formatted with sprintf(). */ led_name = kzalloc(sizeof(char)*20, GFP_KERNEL); if (led_name == NULL) { dev_err(&hdev->dev, G19_NAME " error allocating memory for led %d name", i); error = -ENOMEM; goto err_cleanup_led_structs; } switch (i) { case G19_LED_M1: case G19_LED_M2: case G19_LED_M3: sprintf(led_name, "g19_%d:orange:m%d", hdev->minor, i+1); break; case G19_LED_MR: sprintf(led_name, "g19_%d:red:mr", hdev->minor); break; case G19_LED_BL_R: sprintf(led_name, "g19_%d:red:bl", hdev->minor); break; case G19_LED_BL_G: sprintf(led_name, "g19_%d:green:bl", hdev->minor); break; case G19_LED_BL_B: sprintf(led_name, "g19_%d:blue:bl", hdev->minor); break; case G19_LED_BL_SCREEN: sprintf(led_name, "g19_%d:white:screen", hdev->minor); break; } g19data->led_cdev[i]->name = led_name; } for (i = 0; i < LED_COUNT; i++) { led_num = i; error = led_classdev_register(&hdev->dev, g19data->led_cdev[i]); if (error < 0) { dev_err(&hdev->dev, G19_NAME " error registering led %d", i); error = -EINVAL; goto err_cleanup_registered_leds; } } gdata->gfb_data = gfb_probe(hdev, GFB_PANEL_TYPE_320_240_16); if (gdata->gfb_data == NULL) { dev_err(&hdev->dev, G19_NAME " error registering framebuffer\n"); goto err_cleanup_registered_leds; } dbg_hid("Waiting for G19 to activate\n"); /* Add the sysfs attributes */ error = sysfs_create_group(&(hdev->dev.kobj), &g19_attr_group); if (error) { dev_err(&hdev->dev, G19_NAME " failed to create sysfs group attributes\n"); goto err_cleanup_gfb; } /* * Wait here for stage 1 (substages 1-3) to complete */ wait_for_completion_timeout(&g19data->ready, HZ); /* Protect data->ready_stages before checking whether we're ready to proceed */ spin_lock_irqsave(&gdata->lock, irq_flags); if (g19data->ready_stages != G19_READY_STAGE_1) { dev_warn(&hdev->dev, G19_NAME " hasn't completed stage 1 yet, forging ahead with initialization\n"); /* Force the stage */ g19data->ready_stages = G19_READY_STAGE_1; } init_completion(&g19data->ready); g19data->ready_stages |= G19_READY_SUBSTAGE_4; spin_unlock_irqrestore(&gdata->lock, irq_flags); /* * Send the init report, then follow with the input report to trigger * report 6 and wait for us to get a response. */ g19_feature_report_4_send(hdev, G19_REPORT_4_INIT); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) hid_hw_request(hdev, g19data->start_input_report, HID_REQ_GET_REPORT); #else usbhid_submit_report(hdev, g19data->start_input_report, USB_DIR_IN); #endif wait_for_completion_timeout(&g19data->ready, HZ); /* Protect g19data->ready_stages before checking whether we're ready to proceed */ spin_lock_irqsave(&gdata->lock, irq_flags); if (g19data->ready_stages != G19_READY_STAGE_2) { dev_warn(&hdev->dev, G19_NAME " hasn't completed stage 2 yet, forging ahead with initialization\n"); /* Force the stage */ g19data->ready_stages = G19_READY_STAGE_2; } init_completion(&g19data->ready); g19data->ready_stages |= G19_READY_SUBSTAGE_6; spin_unlock_irqrestore(&gdata->lock, irq_flags); /* * Clear the LEDs */ g19_led_send(hdev); g19data->rgb[0] = G19_DEFAULT_RED; g19data->rgb[1] = G19_DEFAULT_GREEN; g19data->rgb[2] = G19_DEFAULT_BLUE; g19_rgb_send(hdev); g19data->screen_bl = G19_DEFAULT_BRIGHTNESS; g19_screen_bl_send(hdev); /* * Send the finalize report, then follow with the input report to trigger * report 6 and wait for us to get a response. */ g19_feature_report_4_send(hdev, G19_REPORT_4_FINALIZE); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) hid_hw_request(hdev, g19data->start_input_report, HID_REQ_GET_REPORT); hid_hw_request(hdev, g19data->start_input_report, HID_REQ_GET_REPORT); #else usbhid_submit_report(hdev, g19data->start_input_report, USB_DIR_IN); usbhid_submit_report(hdev, g19data->start_input_report, USB_DIR_IN); #endif wait_for_completion_timeout(&g19data->ready, HZ); /* Protect data->ready_stages before checking whether we're ready to proceed */ spin_lock_irqsave(&gdata->lock, irq_flags); if (g19data->ready_stages != G19_READY_STAGE_3) { dev_warn(&hdev->dev, G19_NAME " hasn't completed stage 3 yet, forging ahead with initialization\n"); /* Force the stage */ g19data->ready_stages = G19_READY_STAGE_3; } else { dbg_hid(G19_NAME " stage 3 complete\n"); } spin_unlock_irqrestore(&gdata->lock, irq_flags); ginput_set_keymap_switching(gdata, 1); g19_ep1_read(hdev); dbg_hid("G19 activated and initialized\n"); /* Everything went well */ return 0; err_cleanup_gfb: gfb_remove(gdata->gfb_data); err_cleanup_registered_leds: for (i = 0; i < led_num; i++) led_classdev_unregister(g19data->led_cdev[i]); err_cleanup_led_structs: for (i = 0; i < LED_COUNT; i++) { if (g19data->led_cdev[i] != NULL) { if (g19data->led_cdev[i]->name != NULL) kfree(g19data->led_cdev[i]->name); kfree(g19data->led_cdev[i]); } } err_cleanup_input_dev_reg: input_unregister_device(gdata->input_dev); err_cleanup_input_dev_data: ginput_free(gdata); err_cleanup_input_dev: input_free_device(gdata->input_dev); err_cleanup_hw_start: hid_hw_stop(hdev); err_cleanup_ep1_urb: usb_free_urb(g19data->ep1_urb); err_cleanup_g19data: kfree(g19data); err_cleanup_gdata: kfree(gdata); err_no_cleanup: hid_set_drvdata(hdev, NULL); return error; }