📄 yealink.c
字号:
int i, ret; dbg("%s", __FUNCTION__); /* force updates to device */ for (i = 0; i<sizeof(yld->master); i++) yld->copy.b[i] = ~yld->master.b[i]; yld->key_code = -1; /* no keys pressed */ yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone)); /* issue INIT */ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); yld->ctl_data->cmd = CMD_INIT; yld->ctl_data->size = 10; yld->ctl_data->sum = 0x100-CMD_INIT-10; if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) { dbg("%s - usb_submit_urb failed with result %d", __FUNCTION__, ret); return ret; } return 0;}static void input_close(struct input_dev *dev){ struct yealink_dev *yld = dev->private; usb_kill_urb(yld->urb_ctl); usb_kill_urb(yld->urb_irq);}/******************************************************************************* * sysfs interface ******************************************************************************/static DECLARE_RWSEM(sysfs_rwsema);/* Interface to the 7-segments translation table aka. char set. */static ssize_t show_map(struct device *dev, struct device_attribute *attr, char *buf){ memcpy(buf, &map_seg7, sizeof(map_seg7)); return sizeof(map_seg7);}static ssize_t store_map(struct device *dev, struct device_attribute *attr, const char *buf, size_t cnt){ if (cnt != sizeof(map_seg7)) return -EINVAL; memcpy(&map_seg7, buf, sizeof(map_seg7)); return sizeof(map_seg7);}/* Interface to the LCD. *//* Reading /sys/../lineX will return the format string with its settings: * * Example: * cat ./line3 * 888888888888 * Linux Rocks! */static ssize_t show_line(struct device *dev, char *buf, int a, int b){ struct yealink_dev *yld; int i; down_read(&sysfs_rwsema); yld = dev_get_drvdata(dev); if (yld == NULL) { up_read(&sysfs_rwsema); return -ENODEV; } for (i = a; i < b; i++) *buf++ = lcdMap[i].type; *buf++ = '\n'; for (i = a; i < b; i++) *buf++ = yld->lcdMap[i]; *buf++ = '\n'; *buf = 0; up_read(&sysfs_rwsema); return 3 + ((b - a) << 1);}static ssize_t show_line1(struct device *dev, struct device_attribute *attr, char *buf){ return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET);}static ssize_t show_line2(struct device *dev, struct device_attribute *attr, char *buf){ return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET);}static ssize_t show_line3(struct device *dev, struct device_attribute *attr, char *buf){ return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET);}/* Writing to /sys/../lineX will set the coresponding LCD line. * - Excess characters are ignored. * - If less characters are written than allowed, the remaining digits are * unchanged. * - The '\n' or '\t' char is a placeholder, it does not overwrite the * original content. */static ssize_t store_line(struct device *dev, const char *buf, size_t count, int el, size_t len){ struct yealink_dev *yld; int i; down_write(&sysfs_rwsema); yld = dev_get_drvdata(dev); if (yld == NULL) { up_write(&sysfs_rwsema); return -ENODEV; } if (len > count) len = count; for (i = 0; i < len; i++) setChar(yld, el++, buf[i]); up_write(&sysfs_rwsema); return count;}static ssize_t store_line1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE);}static ssize_t store_line2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE);}static ssize_t store_line3(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE);}/* Interface to visible and audible "icons", these include: * pictures on the LCD, the LED, and the dialtone signal. *//* Get a list of "switchable elements" with their current state. */static ssize_t get_icons(struct device *dev, struct device_attribute *attr, char *buf){ struct yealink_dev *yld; int i, ret = 1; down_read(&sysfs_rwsema); yld = dev_get_drvdata(dev); if (yld == NULL) { up_read(&sysfs_rwsema); return -ENODEV; } for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { if (lcdMap[i].type != '.') continue; ret += sprintf(&buf[ret], "%s %s\n", yld->lcdMap[i] == ' ' ? " " : "on", lcdMap[i].u.p.name); } up_read(&sysfs_rwsema); return ret;}/* Change the visibility of a particular element. */static ssize_t set_icon(struct device *dev, const char *buf, size_t count, int chr){ struct yealink_dev *yld; int i; down_write(&sysfs_rwsema); yld = dev_get_drvdata(dev); if (yld == NULL) { up_write(&sysfs_rwsema); return -ENODEV; } for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { if (lcdMap[i].type != '.') continue; if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) { setChar(yld, i, chr); break; } } up_write(&sysfs_rwsema); return count;}static ssize_t show_icon(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ return set_icon(dev, buf, count, buf[0]);}static ssize_t hide_icon(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ return set_icon(dev, buf, count, ' ');}/* Upload a ringtone to the device. *//* Stores raw ringtone data in the phone */static ssize_t store_ringtone(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct yealink_dev *yld; down_write(&sysfs_rwsema); yld = dev_get_drvdata(dev); if (yld == NULL) { up_write(&sysfs_rwsema); return -ENODEV; } /* TODO locking with async usb control interface??? */ yealink_set_ringtone(yld, (char *)buf, count); up_write(&sysfs_rwsema); return count;}#define _M444 S_IRUGO#define _M664 S_IRUGO|S_IWUSR|S_IWGRP#define _M220 S_IWUSR|S_IWGRPstatic DEVICE_ATTR(map_seg7 , _M664, show_map , store_map );static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 );static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 );static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 );static DEVICE_ATTR(get_icons , _M444, get_icons , NULL );static DEVICE_ATTR(show_icon , _M220, NULL , show_icon );static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon );static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone);static struct attribute *yld_attributes[] = { &dev_attr_line1.attr, &dev_attr_line2.attr, &dev_attr_line3.attr, &dev_attr_get_icons.attr, &dev_attr_show_icon.attr, &dev_attr_hide_icon.attr, &dev_attr_map_seg7.attr, &dev_attr_ringtone.attr, NULL};static struct attribute_group yld_attr_group = { .attrs = yld_attributes};/******************************************************************************* * Linux interface and usb initialisation ******************************************************************************/static const struct yld_device { u16 idVendor; u16 idProduct; char *name;} yld_device[] = { { 0x6993, 0xb001, "Yealink usb-p1k" },};static struct usb_device_id usb_table [] = { { USB_INTERFACE_INFO(USB_CLASS_HID, 0, 0) }, { }};static int usb_cleanup(struct yealink_dev *yld, int err){ if (yld == NULL) return err; if (yld->urb_irq) { usb_kill_urb(yld->urb_irq); usb_free_urb(yld->urb_irq); } if (yld->urb_ctl) usb_free_urb(yld->urb_ctl); if (yld->idev) { if (err) input_free_device(yld->idev); else input_unregister_device(yld->idev); } if (yld->ctl_req) usb_buffer_free(yld->udev, sizeof(*(yld->ctl_req)), yld->ctl_req, yld->ctl_req_dma); if (yld->ctl_data) usb_buffer_free(yld->udev, USB_PKT_LEN, yld->ctl_data, yld->ctl_dma); if (yld->irq_data) usb_buffer_free(yld->udev, USB_PKT_LEN, yld->irq_data, yld->irq_dma); kfree(yld); return err;}static void usb_disconnect(struct usb_interface *intf){ struct yealink_dev *yld; down_write(&sysfs_rwsema); yld = usb_get_intfdata(intf); sysfs_remove_group(&intf->dev.kobj, &yld_attr_group); usb_set_intfdata(intf, NULL); up_write(&sysfs_rwsema); usb_cleanup(yld, 0);}static int usb_match(struct usb_device *udev){ int i; u16 idVendor = le16_to_cpu(udev->descriptor.idVendor); u16 idProduct = le16_to_cpu(udev->descriptor.idProduct); for (i = 0; i < ARRAY_SIZE(yld_device); i++) { if ((idVendor == yld_device[i].idVendor) && (idProduct == yld_device[i].idProduct)) return i; } return -ENODEV;}static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_device *udev = interface_to_usbdev (intf); struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; struct yealink_dev *yld; struct input_dev *input_dev; int ret, pipe, i; i = usb_match(udev); if (i < 0) return -ENODEV; interface = intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; if (!(endpoint->bEndpointAddress & USB_DIR_IN)) return -EIO; if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) return -EIO; yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL); if (!yld) return -ENOMEM; yld->udev = udev; yld->idev = input_dev = input_allocate_device(); if (!input_dev) return usb_cleanup(yld, -ENOMEM); /* allocate usb buffers */ yld->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN, SLAB_ATOMIC, &yld->irq_dma); if (yld->irq_data == NULL) return usb_cleanup(yld, -ENOMEM); yld->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN, SLAB_ATOMIC, &yld->ctl_dma); if (!yld->ctl_data) return usb_cleanup(yld, -ENOMEM); yld->ctl_req = usb_buffer_alloc(udev, sizeof(*(yld->ctl_req)), SLAB_ATOMIC, &yld->ctl_req_dma); if (yld->ctl_req == NULL) return usb_cleanup(yld, -ENOMEM); /* allocate urb structures */ yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL); if (yld->urb_irq == NULL) return usb_cleanup(yld, -ENOMEM); yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL); if (yld->urb_ctl == NULL) return usb_cleanup(yld, -ENOMEM); /* get a handle to the interrupt data pipe */ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); if (ret != USB_PKT_LEN) err("invalid payload size %d, expected %d", ret, USB_PKT_LEN); /* initialise irq urb */ usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data, USB_PKT_LEN, urb_irq_callback, yld, endpoint->bInterval); yld->urb_irq->transfer_dma = yld->irq_dma; yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; yld->urb_irq->dev = udev; /* initialise ctl urb */ yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT; yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION; yld->ctl_req->wValue = cpu_to_le16(0x200); yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN); usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0), (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN, urb_ctl_callback, yld); yld->urb_ctl->setup_dma = yld->ctl_req_dma; yld->urb_ctl->transfer_dma = yld->ctl_dma; yld->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP | URB_NO_TRANSFER_DMA_MAP; yld->urb_ctl->dev = udev; /* find out the physical bus location */ usb_make_path(udev, yld->phys, sizeof(yld->phys)); strlcat(yld->phys, "/input0", sizeof(yld->phys)); /* register settings for the input device */ input_dev->name = yld_device[i].name; input_dev->phys = yld->phys; usb_to_input_id(udev, &input_dev->id); input_dev->cdev.dev = &intf->dev; input_dev->private = yld; input_dev->open = input_open; input_dev->close = input_close; /* input_dev->event = input_ev; TODO */ /* register available key events */ input_dev->evbit[0] = BIT(EV_KEY); for (i = 0; i < 256; i++) { int k = map_p1k_to_key(i); if (k >= 0) { set_bit(k & 0xff, input_dev->keybit); if (k >> 8) set_bit(k >> 8, input_dev->keybit); } } input_register_device(yld->idev); usb_set_intfdata(intf, yld); /* clear visible elements */ for (i = 0; i < ARRAY_SIZE(lcdMap); i++) setChar(yld, i, ' '); /* display driver version on LCD line 3 */ store_line3(&intf->dev, NULL, DRIVER_VERSION, sizeof(DRIVER_VERSION)); /* Register sysfs hooks (don't care about failure) */ sysfs_create_group(&intf->dev.kobj, &yld_attr_group); return 0;}static struct usb_driver yealink_driver = { .owner = THIS_MODULE, .name = "yealink", .probe = usb_probe, .disconnect = usb_disconnect, .id_table = usb_table,};static int __init yealink_dev_init(void){ int ret = usb_register(&yealink_driver); if (ret == 0) info(DRIVER_DESC ":" DRIVER_VERSION); return ret;}static void __exit yealink_dev_exit(void){ usb_deregister(&yealink_driver);}module_init(yealink_dev_init);module_exit(yealink_dev_exit);MODULE_DEVICE_TABLE (usb, usb_table);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -