something_i_am_sure
This commit is contained in:
+234
@@ -0,0 +1,234 @@
|
||||
#include "../include/usb.h"
|
||||
#include "asm-generic/errno-base.h"
|
||||
#include "linux/cleanup.h"
|
||||
#include "linux/device/devres.h"
|
||||
#include "linux/gfp_types.h"
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#define GPIO_PIN_COUNT 18
|
||||
|
||||
struct microcontroller_gpio {
|
||||
struct gpio_chip chip; /* kernel GPIO controller object */
|
||||
struct usb_device *udev; /* handle used for all USB transfers */
|
||||
struct usb_interface *intf;
|
||||
unsigned char ep_addr;
|
||||
int interval;
|
||||
};
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
static int send_cmd_get_response_control(struct usb_device *udev, u8 *dst,
|
||||
__u16 max_resp_size,
|
||||
enum vendor_request request_type,
|
||||
const u8 cmd[2]) {
|
||||
int retval;
|
||||
u16 wValue;
|
||||
if (cmd == NULL) {
|
||||
wValue = ((u16)cmd[0] << 8) | cmd[1]; /* Pack 2 command bytes into wValue */
|
||||
} else {
|
||||
wValue = 0;
|
||||
}
|
||||
|
||||
/* 2. Execute Control IN transfer */
|
||||
retval = usb_control_msg(
|
||||
udev, usb_rcvctrlpipe(udev, 0), /* Control IN pipe */
|
||||
request_type, /* bRequest */
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */
|
||||
wValue, /* 2 Command bytes sent in SETUP */
|
||||
0x0000, /* wIndex (unused) */
|
||||
dst, /* Buffer for incoming data */
|
||||
max_resp_size, /* Request up to 256 bytes */
|
||||
2000 /* Timeout (ms) */
|
||||
);
|
||||
|
||||
if (retval < 0) {
|
||||
pr_err("Control transfer failed: %d\n", retval);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void usb_data_send_completed(struct urb *urb) {
|
||||
if (urb->status == 0)
|
||||
pr_info("Async transfer successful!\n");
|
||||
else
|
||||
pr_err("Async transfer failed with status: %d\n", urb->status);
|
||||
|
||||
kfree(urb->transfer_buffer);
|
||||
}
|
||||
|
||||
static int async_send_usb_data_to_microcontroller(const unsigned char *msg,
|
||||
size_t msg_len,
|
||||
struct usb_device *udev,
|
||||
unsigned char ep_addr,int interval) {
|
||||
struct urb *urb;
|
||||
char *buf_dma;
|
||||
int retval;
|
||||
unsigned int pipe = usb_sndbulkpipe(udev, ep_addr);
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
buf_dma = kzalloc(msg_len, GFP_KERNEL);
|
||||
if (!buf_dma) {
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(buf_dma, msg, msg_len);
|
||||
|
||||
usb_fill_int_urb(urb, udev, pipe, buf_dma, msg_len, usb_data_send_completed, NULL, interval);
|
||||
|
||||
retval = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (retval) {
|
||||
pr_err("Failed to submit URB: %d\n", retval);
|
||||
kfree(buf_dma);
|
||||
}
|
||||
|
||||
// usb_data_send_completed will free is automaticaly
|
||||
usb_free_urb(urb);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Read the level of line "off" from the device; return 1, 0 or -errno. */
|
||||
static int microcontroller_get(struct gpio_chip *gc, unsigned int off) {
|
||||
if (off>= gc->ngpio) {
|
||||
return -EINVAL;
|
||||
}
|
||||
struct microcontroller_gpio *dev = gpiochip_get_data(gc);
|
||||
u8 retval;
|
||||
const u8 cmd[] = {OP_GPIO_GET,off};
|
||||
send_cmd_get_response_control(dev->udev, &retval, sizeof(retval), CONTROL_PINS, cmd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Drive output line "off" to "val" (0 or 1) on the device; return 0 or -errno.
|
||||
*/
|
||||
static int microcontroller_set(struct gpio_chip *gc, unsigned int off,
|
||||
int val) {
|
||||
struct microcontroller_gpio *dev = gpiochip_get_data(gc);
|
||||
const unsigned char msg[2] = {OP_GPIO_SET, val};
|
||||
return async_send_usb_data_to_microcontroller(msg, sizeof(msg), dev->udev,
|
||||
dev->ep_addr,dev->interval);
|
||||
};
|
||||
|
||||
/* Tell the device to make line "off" an input; return 0 or -errno. */
|
||||
static int microcontroller_dir_in(struct gpio_chip *gc, unsigned int off) {
|
||||
struct microcontroller_gpio *dev = gpiochip_get_data(gc);
|
||||
const unsigned char msg[2] = {OP_GPIO_SET_DIR, DIR_IN};
|
||||
return async_send_usb_data_to_microcontroller(msg, sizeof(msg), dev->udev,
|
||||
dev->ep_addr,dev->interval);
|
||||
}
|
||||
|
||||
/* Tell the device to make line "off" an output driving "val"; return 0 or
|
||||
* -errno. */
|
||||
static int microcontroller_dir_out(struct gpio_chip *gc, unsigned int off,
|
||||
int val) {
|
||||
struct microcontroller_gpio *dev = gpiochip_get_data(gc);
|
||||
const unsigned char msg[3] = {OP_GPIO_SET_DIR, DIR_OUT, val};
|
||||
return async_send_usb_data_to_microcontroller(msg, sizeof(msg), dev->udev,
|
||||
dev->ep_addr,dev->interval);
|
||||
}
|
||||
|
||||
static int get_microcontroller_capabilities(struct microcontroller_gpio *dev) {
|
||||
struct microcontroller_capabilities capabs;
|
||||
if (send_cmd_get_response_control(dev->udev, (u8 *)&capabs, sizeof(capabs),
|
||||
GET_INFO, NULL) != sizeof(capabs)) {
|
||||
dev->chip.ngpio = capabs.num_gpio;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when the USB device matches: allocate state, fill gpio_chip, register
|
||||
* it. */
|
||||
static int microcontroller_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id) {
|
||||
struct microcontroller_gpio *dev;
|
||||
dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct usb_host_interface *iface_desc = intf->cur_altsetting;
|
||||
struct usb_endpoint_descriptor *ep_desc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
|
||||
ep_desc = &iface_desc->endpoint[i].desc;
|
||||
|
||||
if (usb_endpoint_is_int_out(ep_desc)) {
|
||||
pr_info("Found Bulk OUT endpoint address: 0x%02X\n",
|
||||
ep_desc->bEndpointAddress);
|
||||
dev->interval = ep_desc->bInterval;
|
||||
dev->ep_addr = ep_desc->bEndpointAddress;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
usb_set_intfdata(intf, dev);
|
||||
dev_info(&intf->dev, "USB device attached: vendor=0x%04x, product=0x%04x\n",
|
||||
le16_to_cpu(dev->udev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->udev->descriptor.idProduct));
|
||||
|
||||
dev->udev = usb_get_dev(interface_to_usbdev(intf));
|
||||
dev->intf = intf;
|
||||
|
||||
dev->chip.label = "microcontroller-gpio";
|
||||
dev->chip.parent = &intf->dev;
|
||||
dev->chip.owner = THIS_MODULE;
|
||||
dev->chip.base = -1;
|
||||
|
||||
dev->chip.get = microcontroller_get;
|
||||
dev->chip.set = microcontroller_set;
|
||||
dev->chip.direction_input = microcontroller_dir_in;
|
||||
dev->chip.direction_output = microcontroller_dir_out;
|
||||
get_microcontroller_capabilities(dev);
|
||||
int ret = devm_gpiochip_add_data(&intf->dev, &dev->chip, dev);
|
||||
if (ret) {
|
||||
dev_err(&intf->dev, "Failed to add gpio_chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called on unplug or driver unbind: release the USB device reference. */
|
||||
static void microcontroller_disconnect(struct usb_interface *intf) {
|
||||
struct microcontroller_gpio *dev;
|
||||
dev = usb_get_intfdata(intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (dev) {
|
||||
usb_put_dev(dev->udev);
|
||||
}
|
||||
}
|
||||
|
||||
/* VID/PID pairs this driver binds to. */
|
||||
static const struct usb_device_id microcontroller_table[] = {
|
||||
{USB_DEVICE(0xfad0, 0x9000)}, {}};
|
||||
/* Exports the table so udev can autoload the module when the device appears. */
|
||||
MODULE_DEVICE_TABLE(usb, microcontroller_table);
|
||||
|
||||
/* Ties probe, disconnect and the ID table together for the USB core. */
|
||||
static struct usb_driver microcontroller_driver = {
|
||||
.name = "microcontroller-GPIO",
|
||||
.probe = microcontroller_probe,
|
||||
.disconnect = microcontroller_disconnect,
|
||||
.id_table = microcontroller_table,
|
||||
};
|
||||
/* Generates module init and exit that register and unregister
|
||||
* microcontroller_driver.
|
||||
*/
|
||||
module_usb_driver(microcontroller_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user