【转载】Linux设备驱动程序

(本部分的一些示例源码来自drivers/usb/usb-skeleton.c,它是Linux内核为我们提供的最基础的USB驱动程序,USB骨架程序)
驱动程序把驱动对象注册到 USB 子系统中,之后使用供应商(idVendor)和设备(idProduct)标识来判断对应的硬件是否已经安装.
驱动的设备支持列表
struct usb_device_id 结构提供了这个驱动支持的不同类型 USB 设备的列表. USB 核心通过此列表用来决定设备对应的驱动,热插拔脚本也通过此列表来决定当特定设备被插入系统时,应该自动加载的驱动.

struct usb_device_id {
/* 确定设备信息去和结构体中哪几个字段匹配来判断驱动的适用性 */
    __u16        match_flags;
/* Used for product specific matches; range is inclusive */
    __u16        idVendor;//USB设备的产品ID,有制造商自定
    __u16        bcdDevice_lo;/* USB设备的产品版本号最低值*/
    __u16        bcdDevice_hi;/* 和最高值,以BCD码来表示。*/

/* 分别定义设备的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中. 这些值指定这个设备的行为, 包括设备上所有的接口 */
    __u8        bDeviceClass;
    __u8        bDeviceSubClass;
    __u8        bDeviceProtocol;

/* 分别定义单个接口的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中 */
    __u8        bInterfaceClass;
    __u8        bInterfaceSubClass;
    __u8        bInterfaceProtocol;

/* 这个值不用来匹配驱动的, 驱动用它来在 USB 驱动的探测回调函数中区分不同的设备 */
    kernel_ulong_t    driver_info;
};


//以上结构体中__u16 match_flags;所使用的define:
//include/linux/mod_devicetable.h
/* Some useful macros to use to create struct usb_device_id */
#define USB_DEVICE_ID_MATCH_VENDOR        0x0001
#define USB_DEVICE_ID_MATCH_PRODUCT        0x0002
#define USB_DEVICE_ID_MATCH_DEV_LO        0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI        0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS        0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS    0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL    0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS        0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS    0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL    0x0200

//include/linux/usb.h
#define USB_DEVICE_ID_MATCH_DEVICE /
(USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
#define USB_DEVICE_ID_MATCH_DEV_RANGE /
(USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)
#define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION /
(USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)
#define USB_DEVICE_ID_MATCH_DEV_INFO /
(USB_DEVICE_ID_MATCH_DEV_CLASS | /
        USB_DEVICE_ID_MATCH_DEV_SUBCLASS | /
        USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
#define USB_DEVICE_ID_MATCH_INT_INFO /
(USB_DEVICE_ID_MATCH_INT_CLASS | /
        USB_DEVICE_ID_MATCH_INT_SUBCLASS | /
        USB_DEVICE_ID_MATCH_INT_PROTOCOL)


//这个结构体一般不用手动赋值,以下的宏可以实现赋值:
/**
 * USB_DEVICE - macro used to describe a specific usb device
 * @vend: the 16 bit USB Vendor ID
 * @prod: the 16 bit USB Product ID
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific device.
 */
//仅和指定的制造商和产品ID匹配,用于需要特定驱动的设备
#define USB_DEVICE(vend,prod) /
.match_flags = USB_DEVICE_ID_MATCH_DEVICE, /
.idVendor = (vend), /
.idProduct = (prod)
/**
 * USB_DEVICE_VER - describe a specific usb device with a version range
 * @vend: the 16 bit USB Vendor ID
 * @prod: the 16 bit USB Product ID
 * @lo: the bcdDevice_lo value
 * @hi: the bcdDevice_hi value
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific device, with a version range.
 */
//仅和某版本范围内的指定的制造商和产品ID匹配
#define USB_DEVICE_VER(vend, prod, lo, hi) /
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, /
.idVendor = (vend), /
.idProduct = (prod), /
.bcdDevice_lo = (lo), /
.bcdDevice_hi = (hi)

/**
 * USB_DEVICE_INTERFACE_PROTOCOL - describe a usb device with a specific interface protocol
 * @vend: the 16 bit USB Vendor ID
 * @prod: the 16 bit USB Product ID
 * @pr: bInterfaceProtocol value
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific interface protocol of devices.
 */
//仅和指定的接口协议、制造商和产品ID匹配
#define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) /
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | /
         USB_DEVICE_ID_MATCH_INT_PROTOCOL, /
.idVendor = (vend), /
.idProduct = (prod), /
.bInterfaceProtocol = (pr)

/**
 * USB_DEVICE_INFO - macro used to describe a class of usb devices
 * @cl: bDeviceClass value
 * @sc: bDeviceSubClass value
 * @pr: bDeviceProtocol value
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific class of devices.
 */
//仅和指定的设备类型相匹配
#define USB_DEVICE_INFO(cl, sc, pr) /
.match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, /
.bDeviceClass = (cl), /
.bDeviceSubClass = (sc), /
.bDeviceProtocol = (pr)

/**
 * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces
 * @cl: bInterfaceClass value
 * @sc: bInterfaceSubClass value
 * @pr: bInterfaceProtocol value
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific class of interfaces.
 */
//仅和指定的接口类型相匹配
#define USB_INTERFACE_INFO(cl, sc, pr) /
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, /
.bInterfaceClass = (cl), /
.bInterfaceSubClass = (sc), /
.bInterfaceProtocol = (pr)

/**
 * USB_DEVICE_AND_INTERFACE_INFO - describe a specific usb device with a class of usb interfaces
 * @vend: the 16 bit USB Vendor ID
 * @prod: the 16 bit USB Product ID
 * @cl: bInterfaceClass value
 * @sc: bInterfaceSubClass value
 * @pr: bInterfaceProtocol value
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific device with a specific class of interfaces.
 *
 * This is especially useful when explicitly matching devices that have
 * vendor specific bDeviceClass values, but standards-compliant interfaces.
 */
//仅和指定的制造商、产品ID和接口类型相匹配
#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) /
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO /
| USB_DEVICE_ID_MATCH_DEVICE, /
.idVendor = (vend), /
.idProduct = (prod), /
.bInterfaceClass = (cl), /
.bInterfaceSubClass = (sc), /
.bInterfaceProtocol = (pr)
/* ----------------------------------------------------------------------- */

 

 

 

对于一个只为一个供应商的一个 USB 设备的简单 USB 设备驱动, 其 struct usb_device_id 可定义如下:

/* Define these values to match your devices */
#define USB_SKEL_VENDOR_ID    0xfff0
#define USB_SKEL_PRODUCT_ID    0xfff0

/* table of devices that work with this driver */
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ }/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);

 

 

 

MODULE_DEVICE_TABLE 宏是必需的,它允许用户空间工具判断该驱动可控制什么设备. 对于 USB 驱动, 这个宏中的第一个值必须是 usb .
如果你需要这个驱动被系统中每个 USB 设备调用, 创建仅需设置 driver_info 成员:

static struct usb_device_id usb_ids[] = {
{.driver_info = 42},
{}
};

 

 

 

注册USB驱动程序
所有 USB 驱动都必须创建的主要结构是 struct usb_driver. 这个结构必须被 USB 驱动程序手动填充并且包含多个回调函数和变量, 并向 USB 核心描述 USB 驱动程序:

struct usb_driver {
const char *name;
/*指向驱动程序名字的指针. 它必须在内核所有的 USB 驱动中是唯一的(通常被设为和驱动模块名相同).当驱动在内核中运行时,会出现在/sys/bus/usb/drivers目录中 */

int (*probe) (struct usb_interface *intf,
         const struct usb_device_id *id);
/*指向 USB 驱动中探测函数指针. 当USB 核心认为它有一个本驱动可处理的 struct usb_interface时此函数将被调用. USB 核心用来做判断的 struct usb_device_id 指针也被传递给此函数.如果这个 USB 驱动确认传递给它的 struct usb_interface, 它应当正确地初始化设备并返回 0. 如果驱动没有确认这个设备或发生错误,则返回负错误值 */

void (*disconnect) (struct usb_interface *intf);
/*指向 USB 驱动的断开函数指针.当 struct usb_interface 从系统中清除或驱动 USB 核心卸载时,函数将被 USB 核心调用*/

int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
/*指向 USB 驱动的 ioctl 函数指针. 若此函数存在, 在用户空间程序对usbfs 文件系统关联的设备调用 ioctl 时,此函数将被调用. 实际上,当前只有 USB 集线器驱动使用这个 ioctl*/

int (*suspend) (struct usb_interface *intf, pm_message_t message);
/*指向 USB 驱动中挂起函数的指针*/
int (*resume) (struct usb_interface *intf);
/*指向 USB 驱动中恢复函数的指针*/
int (*reset_resume)(struct usb_interface *intf);
/*要复位一个已经被挂起的USB设备时调用此函数*/

int (*pre_reset)(struct usb_interface *intf);
/*在设备被复位之前由usb_reset_composite_device()调用*/
int (*post_reset)(struct usb_interface *intf);
/*在设备被复位之后由usb_reset_composite_device()调用*/

const struct usb_device_id *id_table;
/*指向 struct usb_device_id 表的指针*/

struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
/*是struct device_driver driver的再包装,struct device_driver 包含 struct module *owner;*/
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int soft_unbind:1;
};
#define    to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)

 

 

 

创建一个简单的 struct usb_driver 结构, 只有 4 个成员需要初始化:

static struct usb_driver skel_driver = {
.name = "skeleton",
.id_table = skel_table,
.probe = skel_probe,
.disconnect = skel_disconnect,
};

 

 

 

 

//向 USB 核心注册 struct usb_driver
static int __init usb_skel_init(void)
{
int result;
/* register this driver with the USB subsystem */
        result = usb_register(&skel_driver);
if (result)
                err("usb_register failed. Error number %d", result);
return result;
}

/*当 USB 驱动被卸载, struct usb_driver 需要从内核注销(代码如下). 当以下调用发生, 当前绑定到这个驱动的任何 USB 接口将会断开, 并调用断开函数*/
static void __exit usb_skel_exit(void)
{
/* deregister this driver with the USB subsystem */
        usb_deregister(&skel_driver);
}

 

 

 

探测和断开的细节

在 struct usb_driver 结构中, 有 2 个 USB 核心在适当的时候调用的函数:
(1)当设备安装时, 如果 USB 核心认为这个驱动可以处理,则调用探测(probe)函数,探测函数检查传递给它的设备信息, 并判断驱动是否真正合适这个设备.
(2)由于某些原因,设备被移除或驱动不再控制设备时,调用断开(disconnect)函数,做适当清理.

探测和断开回调函数都在 USB 集线器内核线程上下文中被调用, 因此它们休眠是合法的. 为了缩短 USB 探测时间,大部分工作尽可能在设备打开时完成.这是因为 USB 核心是在一个线程中处理 USB 设备的添加和移除, 因此任何慢设备驱动都可能使 USB 设备探测时间变长。

   探测函数分析
在探测回调函数中, USB 驱动应当初始化它可能用来管理 USB 设备的所有本地结构并保存所有需要的设备信息到本地结构, 因为在此时做这些通常更容易.为了和设备通讯,USB 驱动通常要探测设备的端点地址和缓冲大小. 以下是usb-skeleton的probe函数中的探测代码:

/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
    iface_desc = interface->cur_altsetting;//从输入的interface中提取当前接口的端点总数
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
/*轮询所有端点*/
        endpoint = &iface_desc->endpoint[i].desc;//获得端点的数据结构指针

if (!dev->bulk_in_endpointAddr &&
         usb_endpoint_is_bulk_in(endpoint)) {//如果是批量输入端点,
/* we found a bulk in endpoint */
            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
            dev->bulk_in_size = buffer_size;//获得端点大小
            dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;//获得端点地址
            dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);//为此端点创建缓冲区

if (!dev->bulk_in_buffer) {
                err("Could not allocate bulk_in_buffer");
goto error;
}
}

if (!dev->bulk_out_endpointAddr &&
         usb_endpoint_is_bulk_out(endpoint)) {    如果是批量输出端点
/* we found a bulk out endpoint */
            dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;//获得端点地址
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {//如果不是这两种端点,报错
        err("Could not find both bulk-in and bulk-out endpoints");
goto error;
}

//这里端点判断的函数给我们的编程带来了方便:
/*-------------------------------------------------------------------------*/
/**
 * usb_endpoint_num - get the endpoint's number
 * @epd: endpoint to be checked
 *
 * Returns @epd's number: 0 to 15.
 */
static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd)
{
return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
}

/**
 * usb_endpoint_type - get the endpoint's transfer type
 * @epd: endpoint to be checked
 *
 * Returns one of USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT} according
 * to @epd's transfer type.
 */
static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd)
{
return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
}

/**
 * usb_endpoint_dir_in - check if the endpoint has IN direction
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint is of type IN, otherwise it returns false.
 */
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{
return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}

/**
 * usb_endpoint_dir_out - check if the endpoint has OUT direction
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint is of type OUT, otherwise it returns false.
 */
static inline int usb_endpoint_dir_out(
const struct usb_endpoint_descriptor *epd)
{
return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
}

/**
 * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint is of type bulk, otherwise it returns false.
 */
static inline int usb_endpoint_xfer_bulk(
const struct usb_endpoint_descriptor *epd)
{
return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
        USB_ENDPOINT_XFER_BULK);
}

/**
 * usb_endpoint_xfer_control - check if the endpoint has control transfer type
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint is of type control, otherwise it returns false.
 */
static inline int usb_endpoint_xfer_control(
const struct usb_endpoint_descriptor *epd)
{
return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
        USB_ENDPOINT_XFER_CONTROL);
}

/**
 * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint is of type interrupt, otherwise it returns
 * false.
 */
static inline int usb_endpoint_xfer_int(
const struct usb_endpoint_descriptor *epd)
{
return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
        USB_ENDPOINT_XFER_INT);
}

/**
 * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint is of type isochronous, otherwise it returns
 * false.
 */
static inline int usb_endpoint_xfer_isoc(
const struct usb_endpoint_descriptor *epd)
{
return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
        USB_ENDPOINT_XFER_ISOC);
}

/**
 * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint has bulk transfer type and IN direction,
 * otherwise it returns false.
 */
static inline int usb_endpoint_is_bulk_in(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));
}

/**
 * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint has bulk transfer type and OUT direction,
 * otherwise it returns false.
 */
static inline int usb_endpoint_is_bulk_out(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));
}

/**
 * usb_endpoint_is_int_in - check if the endpoint is interrupt IN
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint has interrupt transfer type and IN direction,
 * otherwise it returns false.
 */
static inline int usb_endpoint_is_int_in(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
}

/**
 * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint has interrupt transfer type and OUT direction,
 * otherwise it returns false.
 */
static inline int usb_endpoint_is_int_out(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
}

/**
 * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint has isochronous transfer type and IN direction,
 * otherwise it returns false.
 */
static inline int usb_endpoint_is_isoc_in(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));
}

/**
 * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT
 * @epd: endpoint to be checked
 *
 * Returns true if the endpoint has isochronous transfer type and OUT direction,
 * otherwise it returns false.
 */
static inline int usb_endpoint_is_isoc_out(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));
}

/*-------------------------------------------------------------------------*/

 

 

 

在设备注册之后,USB 驱动的后续操作都是通过struct usb_interface 获得设备的端点信息,所以要使用 usb_set_intfdata 将前面获得的端点信息保存到struct usb_interface 下的struct device 中的void *driver_data;中,以方便以后的操作。在usb-skeleton的probe函数中的代码:

/* save our data pointer in this interface device */
    usb_set_intfdata(interface, dev);

 

 

 

之后在USB的驱动程序中的打开函数和断开函数中调用usb_get_intfdata来获取端点数据。由于这 2 个函数, USB 驱动不需要为系统中所有当前的设备各保持一个静态指针数组来保存单个设备结构. 对设备信息的非直接引用使得任何 USB 驱动都支持不限数量的设备.

若这个 USB 驱动没有和另一种处理用户和设备交互的子系统(如 input, tty, video......)关联, 驱动可使用 USB 主设备号,以便在用户空间使用传统的字符驱动接口. 为此, USB 驱动必须在探测函数中调用 usb_register_dev 函数, 以注册一个设备到 USB 核心. 在usb-skeleton的probe函数中的代码:

/* we can register the device now, as it is ready */
    retval = usb_register_dev(interface, &skel_class);
if (retval) {
/* something prevented us from registering this driver */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
goto error;
}

//其中使用到的 struct usb_class_driver 结构体如下:
/**
 * struct usb_class_driver - identifies a USB driver that wants to use the USB major number
 * @name: the usb class device name for this driver. Will show up in sysfs.
 * @fops: pointer to the struct file_operations of this driver.
 * @minor_base: the start of the minor range for this driver.
 *
 * This structure is used for the usb_register_dev() and
 * usb_unregister_dev() functions, to consolidate a number of the
 * parameters used for them.
 */
struct usb_class_driver {
char *name;//sysfs 用来描述设备的名字

const struct file_operations *fops;// struct file_operations 结构指针, 驱动定义来注册为字符设备
int minor_base;
/*给这个驱动安排的次设备号的起始. 所有和这个驱动相关的设备被创建为从这个值开始的唯一的, 递增的次设备号. 只有 16 个设备被允许在任何时刻和这个驱动关联, 除非 CONFIG_USB_DYNAMIC_MINORS 配置选项被打开. 如

果这样, 忽略这个变量, 并且这个设备的所有的次设备号会以先来先服务的方式分配. 建议打开了这个选项的系统使用类似 udev 的程序来产生系统中的设备节点, 因为一个静态的 /dev 树不会正确工作.*/
};

//而在usb-skeleton的设置如下:
static const struct file_operations skel_fops = {
.owner =    THIS_MODULE,
.read =        skel_read,
.write =    skel_write,
.open =        skel_open,
.release =    skel_release,
.flush =    skel_flush,
};
/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver skel_class = {
.name ="skel%d",
.fops =&skel_fops,
.minor_base =    USB_SKEL_MINOR_BASE,
};

 

 

 

当 USB 设备断开, 所有关联到这个设备的资源都应被释放,如果已在探测函数中调用 usb_register_dev 分配了 USB 设备的次设备号, 必须调用函数 usb_deregister_dev 来将次设备号还回 USB 核心.在断开函数中, 需要从接口获取之前调用 usb_set_intfdata 所设置的数据,然后设置struct usb_interface 结构指针为 NULL,以防止错误的访问.而在usb-skeleton的源码如下:

static void skel_disconnect(struct usb_interface *interface)
{
struct usb_skel *dev;
int minor = interface->minor;

    dev = usb_get_intfdata(interface);
    usb_set_intfdata(interface, NULL);

/* give back our minor */
    usb_deregister_dev(interface, &skel_class);

/* prevent more I/O from starting */
    mutex_lock(&dev->io_mutex);
    dev->interface = NULL;
    mutex_unlock(&dev->io_mutex);

    usb_kill_anchored_urbs(&dev->submitted);

/* decrement our usage count */
    kref_put(&dev->kref, skel_delete);

    info("USB Skeleton #%d now disconnected", minor);
}

 

 

 

当一个 USB 设备调用 disconnect 函数时, 所有当前正被传送的 urb 可自动被 USB 核心取消, 不必显式调用 usb_kill_urb. 在USB设备被断开之后,如果驱动试图调用 usb_submit_urb 提交urb , 将会失败, 错误值为-EPIPE.

提交和控制 urb 的过程

以usb-skeleton源码中的写函数为例:

static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;
size_t writesize = min(count, (size_t)MAX_TRANSFER);

    dev = (struct usb_skel *)file->private_data;

/* verify that we actually have some data to write */
if (count == 0)
goto exit;

/* limit the number of URBs in flight to stop a user from using up all RAM */
if (down_interruptible(&dev->limit_sem)) {
        retval = -ERESTARTSYS;
goto exit;
}

    spin_lock_irq(&dev->err_lock);
if ((retval = dev->errors) < 0) {
/* any error is reported once */
        dev->errors = 0;
/* to preserve notifications about reset */
        retval = (retval == -EPIPE) ? retval : -EIO;
}
    spin_unlock_irq(&dev->err_lock);
if (retval < 0)
goto error;

/* create a urb, and a buffer for it, and copy the data to the urb */
/*当驱动有数据发送到 USB 设备,首先分配一个 urb */
    urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
        retval = -ENOMEM;
goto error;
}

/*以最有效的方式是创建一个 DMA 缓冲区来发送数据到设备, 并拷贝数据到缓冲区*/
    buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
        retval = -ENOMEM;
goto error;
}

if (copy_from_user(buf, user_buffer, writesize)) {
        retval = -EFAULT;
goto error;
}

/* this lock makes sure we don't submit URBs to gone devices */
    mutex_lock(&dev->io_mutex);
if (!dev->interface) {/* disconnect() was called */
        mutex_unlock(&dev->io_mutex);
        retval = -ENODEV;
goto error;
}

/* initialize the urb properly */
/*在将urb提交给 USB 核心之前,正确初始化 urb */
    usb_fill_bulk_urb(urb, dev->udev,
             usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
             buf, writesize, skel_write_bulk_callback, dev);
    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    usb_anchor_urb(urb, &dev->submitted);

/* send the data out the bulk port */
/*提交 urb 给 USB 核心, 由它将 urb 传递给设备*/
    retval = usb_submit_urb(urb, GFP_KERNEL);
    mutex_unlock(&dev->io_mutex);
if (retval) {
        err("%s - failed submitting write urb, error %d", __func__, retval);
goto error_unanchor;
}

/* release our reference to this urb, the USB core will eventually free it entirely */
    usb_free_urb(urb);


return writesize;

error_unanchor:
    usb_unanchor_urb(urb);
error:
if (urb) {
        usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
        usb_free_urb(urb);
}
    up(&dev->limit_sem);

exit:
return retval;
}

//当urb被成功传递到 USB 设备(或者在传输中发生了错误), urb 回调函数将被 USB 核心调用.也就是上面初始化 urb 中的 skel_write_bulk_callback
static void skel_write_bulk_callback(struct urb *urb)
{
struct usb_skel *dev;

    dev = urb->context;

/* sync/async unlink faults aren't errors */
/*检查 urb 的状态,判断这个 urb 是否成功完成传输*/
if (urb->status) {
if(!(urb->status == -ENOENT ||
         urb->status == -ECONNRESET ||
         urb->status == -ESHUTDOWN))
            err("%s - nonzero write bulk status received: %d",
             __func__, urb->status);

        spin_lock(&dev->err_lock);
        dev->errors = urb->status;
        spin_unlock(&dev->err_lock);
}

/* free up our allocated buffer */
/*释放分配给这个 urb 的缓冲区.*/
    usb_buffer_free(urb->dev, urb->transfer_buffer_length,
            urb->transfer_buffer, urb->transfer_dma);
    up(&dev->limit_sem);
}

 

 

 

urb 回调函数是在中断上下文运行, 因此它不应做任何内存分配, 持有任何信号量, 或任何可导致进程休眠的事情. 如果从回调中提交 urb 并需要分配新内存块, 需使用 GFP_ATOMIC 标志来告知 USB 核心不要休眠.

使用简单的函数接口(urb函数的包装)

有时只是要发送或接受一些简单的 USB 数据,可以使用简单的函数接口:

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
         void *data, int len, int *actual_length, int timeout)
/*创建批量 urb 并发送到指定的设备, 接着在返回之前等待完成.*/
//struct usb_device *usb_dev :目标 USB 设备指针
//unsigned int pipe :目标 USB 设备的特定端点. 必须使用特定的宏创建.
//void *data :如果是 OUT 端点, 指向要发送到设备的数据的指针. 如果是 IN 端点, 这是从设备读取的数据的缓冲区指针.
//int len : data 参数指向的缓冲的长度
//int *actual_length :指向函数放置真实字节数的指针,根据端点方向,这些字节要么是被发送到设备的,要么是从设备中读取的.
//int timeout :时钟嘀哒数, 应等待的时间. 如果为 0, 函数永远等待操作完成.
//成功返回0,actual_length 参数包含被传送或从设备中读取的字节数.否则返回负的错误值.

int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
         __u8 requesttype, __u16 value, __u16 index, void *data,
         __u16 size, int timeout)
/*创建控制 urb 并发送到指定的设备, 接着在返回之前等待完成.*/
//struct usb_device *usb_dev :目标 USB 设备指针
//unsigned int pipe :目标 USB 设备的特定端点. 必须使用特定的宏创建.
//__u8 request :控制消息的 USB 请求值.
//__u8 requesttype :控制消息的 USB 请求类型.
//__u16 value :控制消息的 USB 消息值.
//__u16 index :控制消息的 USB 消息索引值.
//void *data :如果是 OUT 端点, 指向要发送到设备的数据的指针. 如果是 IN 端点, 这是从设备读取的数据的缓冲区指针.
//__u16 size : data 参数指向的缓冲的长度
//int timeout :时钟嘀哒数, 应等待的时间. 如果为 0, 函数永远等待操作完成.
//成功返回被传送到或从设备读取的字节数.否则返回负的错误值.

int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
/*创建中断 urb 并发送到指定的设备, 接着在返回之前等待完成.其实就是usb_bulk_msg的包装,所有参数和usb_bulk_msg一样使用*/

 

 

 

以上的函数不能在中断上下文或持有自旋锁时调用. 这些函数不能被取消, 所以小心使用; 确保驱动的 disconnect 函数了解足够的信息, 在允许它自己从内存被卸载之前等待调用结束.


 其他 USB 函数

USB 核心中的一些辅助函数用来从所有的 USB 设备中获取标准信息. 这些函数不能在中断上下文或者持有自旋锁时调用,因为他们内部都是使用上面介绍的简单的接口函数.这里就不一一介绍了,包括《LDD3》介绍的这些函数,在/drivers/usb/core/message.c都有。

发表于: 2009-07-15,修改于: 2009-07-16 19:36,已浏览1127次,有评论0条 推荐 投诉

网友评论
好文章,我是从你的第一章开始看的,今天9号看到你又有新内容,学不少东西,请坚持下去,加油啊

 

很不错啊,我几乎每天都来学习你的经验,感觉收益非浅,加油啊!

 

我觉得最好的方式就是有人交流^_^我很想与你交流,我的QQ32313055

 

我学arm一年多了,可是水平比阁下差远了。看完文章,收获良多,非常感谢!

 


向Tekkaman  Ninja致敬,你的学习态度和学习方式都值得我们借鉴!

 


圣诞快乐!让我学到了很多东西,

 

很好的文章啊,我正在学习驱动开发,刚刚入门,谢谢啊










write error! code=-1 write ok! code=21 read ok! code=20 [0]=0 [1]=0 [2]=1 [3]=2 [4]=3[5]=4 [6]=5 [7]=6 [8]=7 [9]=8[10]=9 [11]=10 [12]=11 [13]=12 [14]=13[15]=14 [16]=15 [17]=16 [18]=17 [19]=18

 

我改了一下makefile在pc上运行是这个结果


所有的源码都在友善之臂SBC2440V4上反复测试过(内核为2.6.22.2)。所以基本上只要改Makefile就好(除了硬件相关的部分)。如果有问题可以将详细的情况发邮件给我,有空我会回复的。

 

 

 



写的真的很好我们老师就让写一个字符设备驱动程序,我都是通过在你的网页上学会的所有的东西。

 



赞一个,总结的很好曾经研究过ldd3,没有认真写下读书笔记,以后还得好好再看下了

 

请问怎么使用drivers/spi/atmel_spi.c提供的驱动呢?在/dev下没有相应的设备节点,怎么才能在用户空间访问设备?如果自己写一个驱动注册一个spi驱动可以使用其中哪些函数接口?诚请帮忙

 



今天看了你的文章很感慨,希望能共同学习。有个问题想问,请问当insmod scull.ko scull_quantum=6 scull_qset=2数据已经溢出了,为什么20个数据还能写进去。谢谢了


这个和scull的数据结构有关,你认真看看《Linux设备驱动程序学习(1)-字符设备驱动程序》,中的图,你可能对scull的数据结构还没有理解透,scull_quantum代表了一个quantum的字节数,scull_qset代表一个qset包含几个quantum,但是还有qset没有限制,也就是说我写20个字节进去,一个scull中有2个qset。

 

 

 

 



总结的太好了。有一个问题想请教一下。看模块中定义了一个char *whom = "world"这是一个字符串常量,如果按照标准C的话,指针指向的是一个常量字符串才对,其中的内容不能修改。比如whom[2]='a';这样会有段错误,为什么在内核中这样操作是合法的呢? 


我觉得你这样是定义一个字符串变量,所以可以修改。我学硬件的,对c研究还不深,不知见解是否正确?

 

 

 

 

 lz的经验和态度值得我们学习,向你致敬!

 

很不错,尤其是有源码和测试程序。

 






你好!刚接触linux驱动,看到你这里的设备模型分析,很不错帮了我的大忙!我有一个问题,对于nand flash,作为一个块设备,应用程序如何去访问它,对它进行擦除、读写等操作?因为它不象字符设备那样有可供调用的系统调用


块设备我暂时还没看,见谅

 

 

 

 

我也想知道块设备的一些知识,老是来你这儿看,希望能得到一些帮助,呵呵


块设备我还没看啊,苦于没时间和没板做实验

 

 

 

 








问一个问题:你上文提到的测试程序是不是相当于linux系统的应用程序,实现对驱动的系统调用?如果是这样,那么测试程序应该放在什么路径下,用什么编译器进行编译,因为我看到你有写测试程序的makefile,是不是也要用交叉编译器对它进行编译阿,可是这样的话,生成的目标代码是arm格式的,不能在pc上直接执行。麻烦你帮我解答一下这个疑问。刚刚接触linux,问得问题比较愚蠢,见笑了


测试程序是应用程序,在板上运行
要交叉编译

 

 

 

 

总结得很好   就像上完课了要复习  

 

很好的学习总结,受益匪浅!

 








[root@(none) scullc]# insmod scullc.koUsing scullc.konfs: server 222.31.45.9 not responding, still tryingnfs: server 222.31.45.9 not responding, still tryingnfs: server 222.31.45.9 not responding, still trying我按照傅大哥写的模块,一装载的时候就出错了........里面的网络没问题,就是装载了模块后出问题的........傅大哥知道怎么回事吗???????




我是刚接触linux的。今天看到你的总接非常高兴,做的太详细了。。。我想问一下,是否可以讲解一下container_of是怎么用的。。谢谢!

 

























关于楼主描述的:最关键的“syslog(8,NULL,level)”语句我不理解,没有找到相关资料。但是通过在ARM9板上的实验表明:程序是ok的!我用Hello world模块做了实验,现象和书上的一致。我查阅了一下,应该这么理解:原型是:int syslog(int type, char *bufp, int len);根据type来采取对内核ring buffer的操作,并设置consele_loglevel的值为len。type参数有以下几种值:       /*        * Commands to sys_syslog:        *        *      0 -- Close the log.  Currently a NOP.        *      1 -- Open the log. Currently a NOP.        *      2 -- Read from the log.        *      3 -- Read up to the last 4k of messages in the ring buffer.        *      4 -- Read and clear last 4k of messages in the ring buffer        *      5 -- Clear ring buffer.        *      6 -- Disable printk’s to console        *      7 -- Enable printk’s to console        *      8 -- Set level of messages printed to console        *      9 -- Return number of unread characters in the log buffer        */返回值的描述:       In case of error, -1 is returned, and errno is set. Otherwise,       for  type  equal  to 2, 3 or 4, syslog() returns the number of       bytes read, and otherwise 0.不妥之处请指教,谢谢。

 

文章写的很好,刚开始学驱动,有很多地方都不是很懂,很想跟你学习啊,想请教一下学习linux设备驱动的方法。

 

问一个很弱的问题,楼主的源码文件是如何存储在cublog里面的,敬请告知,不胜感激
































write code=6write code=6write code=6write code=2read code=6read code=6read code=6read code=2[0]=0 [1]=1 [2]=2 [3]=3 [4]=4[5]=5 [6]=6 [7]=7 [8]=8 [9]=9[10]=10 [11]=11 [12]=12 [13]=13 [14]=14[15]=15 [16]=16 [17]=17 [18]=18 [19]=19 看你的测试程序中 输出语句 要么是 work error! code=..   或者 work ok! code=..    上面的代码输出 work  code=...   既没有error! 也没有ok! 什么原因啊?在字符驱动那张图 scull_device所指的链表中 Scull_qset 这种结构的个数如何确定呢。insmod scull_debug.ko scull_nr_devs=1 scull_quantum=6 scull_qset=2后我测试的结果是:write error! code=6write error! code=6write error! code=6write ok! code=2read error! code=6read error! code=6read error! code=6read ok! code=2[0]=0 [1]=1 [2]=2 [3]=3 [4]=4[5]=5 [6]=6 [7]=7 [8]=8 [9]=9[10]=10 [11]=11 [12]=12 [13]=13 [14]=14[15]=15 [16]=16 [17]=17 [18]=18 [19]=19请问如何解释啊。



从楼主这里学到不少东西void kobject_del(struct kobject *kobj); /*是 kobject_del 和 kobject_put 的结合*/写错了

 

























编译的时候出现[root@localhost scull]# make modulesmake -C /home/alu/mywork/systems/linux-2.6.22.6  M=/home/soso/ldd3/scull   modulesmake[1]: Entering directory `/home/alu/mywork/systems/linux-2.6.22.6'  CC [M]  /home/soso/ldd3/scull/scull_t1.occ1: invalid option `little-endian'cc1: invalid option `arm'cc1: invalid option `apcs'cc1: invalid option `no-sched-prolog'cc1: invalid option `apcs-32'cc1: invalid option `no-thumb-interwork'cc1: invalid option `tune=arm9tdmi'cc1: invalid option `alignment-traps'cc1: unrecognized option `-Wdeclaration-after-statement'make[2]: *** [/home/soso/ldd3/scull/scull_t1.o] 错误 1make[1]: *** [_module_/home/soso/ldd3/scull] 错误 2make[1]: Leaving directory `/home/alu/mywork/systems/linux-2.6.22.6'make: *** [modules] 错误 2请教大哥  这个大概可能是什么问题啊  那么多参数无效  内核版本2.6.22.6 arm-linux-gcc -vgcc version 3.3.6

 









"include/linux/moduleparam.h文件中有关于module_param_array宏的说明,这里第三个参数的作用是:*nump is set to number they actually specified所以这里的TNparam_nr并不是限制输入数组参数的数目,而是实际数据的大小。可以在上面的示例代码中加一句:printk(KERN_ALERT "TNparam_nr=%d/n",TNparam_nr);得到的结果会是你实际输入的数组元素个数。BTW:笔记写得这么详细,我都懒得写了,直接收藏你的笔记了:-)


感谢兄台指点!!!!!!!

 

 

 

 

新学驱动开发   跟着ldd3看不懂  来支持下你   

 















    /* Then follow the list */    while (n--) {        if (!qs->next) {            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);            if (qs->next == NULL)                return NULL;  /* Never mind */            memset(qs->next, 0, sizeof(struct scull_qset));        }        qs = qs->next;        continue;    }    return qs;}continue;有什么作用


正如注释所说的: 是不断的跟随双向链表到底!

 

 

 

 
























 网友: 本站网友  时间:2009-04-12 09:43:54 IP地址:159.226.139.★        /* Then follow the list */    while (n--) {        if (!qs->next) {            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);            if (qs->next == NULL)                return NULL;  /* Never mind */            memset(qs->next, 0, sizeof(struct scull_qset));        }        qs = qs->next;        continue;    }    return qs;}continue;有什么作用Blog作者的回复:正如注释所说的: 是不断的跟随双向链表到底! 去掉continue;依然可以继续循环,continue是多余的???  

 







 Tekkaman:            你好,首先向你的工作致敬,我很希望和你一起探讨内核题,对于 文中提到“我这个实验除了对参数的改变进行实验外,我的一个重要的目的是测试“ module_param_array(TNparam , int , &TNparam_nr , S_IRUGO);”中&TNparam_nr对输入参数数目的限制作用。经过我的实验,表明&TNparam_nr并没有对输入参数的数目起到限制作用。真正起到限制作用的是“static int TNparam[] = {1,2,3,4};”本身定义的大小,我将程序进行修改:”  我作了测试,当insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1,5,时,系统报错“insmod:error inserting 'paramadd.ko":-1 Invalid parameters说明&TNparam_nr有对输入参数的数目起到限制作用,我用的是Fedora8

 

博主你是我见过的最好的博主了!永远支持你!

 


























































































































































man syslog  如下:NAME       syslog,  klogctl  -  read  and/or clear kernel message ring buffer; set       console_loglevelSYNOPSIS       int syslog(int type, char *bufp, int len);                       /* No wrapper provided in glibc */       /* The glibc interface */       #include <sys/klog.h>       int klogctl(int type, char *bufp, int len);DESCRIPTION       If you need the libc function syslog()  (which  talks  to  syslogd(8)),       then look at syslog(3).  The system call of this name is about control‐       ling the kernel printk()  buffer,  and  the  glibc  version  is  called       klogctl().       The type argument determines the action taken by this function.       Quoting from kernel/printk.c:       /*        * Commands to sys_syslog:        *        *      0 -- Close the log.  Currently a NOP.        *      1 -- Open the log. Currently a NOP.        *      2 -- Read from the log.        *      3 -- Read all messages remaining in the ring buffer.        *      4 -- Read and clear all messages remaining in the ring buffer        *      5 -- Clear ring buffer.        *      6 -- Disable printk to console        *      7 -- Enable printk to console        *      8 -- Set level of messages printed to console        *      9 -- Return number of unread characters in the log buffer        *     10 -- Return size of the log buffer        */       Only  command types 3 and 10 are allowed to non-root processes.  Type 9       was added in 2.4.10; type 10 in 2.6.6.   The kernel log buffer       The kernel has a cyclic buffer of length LOG_BUF_LEN in which  messages       given  as arguments to the kernel function printk() are stored (regard‐       less of their loglevel).  In early kernels, LOG_BUF_LEN had  the  value       4096;  from  kernel  1.3.54,  it  was  8192; from kernel 2.1.113 it was       16384; since 2.4.23/2.6 the value is a kernel configuration option.  In       recent kernels the size can be queried with command type 10.       The  call  syslog(2,buf,len)  waits  until  this  kernel  log buffer is       nonempty, and then reads at most len bytes into  the  buffer  buf.   It       returns  the  number  of bytes read.  Bytes read from the log disappear       from the log buffer: the information can only be read  once.   This  is       the  function  executed  by  the  kernel  when  a  user  program  reads       /proc/kmsg.       The call syslog(3,buf,len) will read the last len bytes  from  the  log       buffer  (non-destructively),  but  will  not read more than was written       into the buffer since the last "clear ring buffer" command (which  does       not clear the buffer at all).  It returns the number of bytes read.       The  call  syslog(4,buf,len) does precisely the same, but also executes       the "clear ring buffer" command.       The call syslog(5,dummy,dummy) executes just the  "clear  ring  buffer"       command.  (In each call where buf or len is shown as "dummy", the value       of the argument is ignored by the call.)       The call syslog(6,dummy,dummy) sets the console log level  to  minimum,       so that no messages are printed to the console.       The  call  syslog(7,dummy,dummy) sets the console log level to default,       so that messages are printed to the console.       The call syslog(8,dummy,level) sets the console  log  level  to  level,       which must be an integer between 1 and 8 (inclusive).  See the loglevel       section for details.       The call syslog(9,dummy,dummy) returns the number  of  bytes  currently       available to be read on the kernel log buffer.       The  call  syslog(10,dummy,dummy)  returns the total size of the kernel       log buffer.   The loglevel       The kernel routine printk() will only print a message on  the  console,       if  it  has  a  loglevel  less  than  the  value  of  the variable con‐       sole_loglevel.  This variable  initially  has  the  value  DEFAULT_CON‐       SOLE_LOGLEVEL (7), but is set to 10 if the kernel command line contains       the word "debug", and to 15 in case of a kernel fault (the  10  and  15       are just silly, and equivalent to 8).  This variable is set (to a value       in the range 1-8) by the call syslog(8,dummy,value).   The  calls  sys‐       log(type,dummy,dummy)  with  type  equal to 6 or 7, set it to 1 (kernel       panics only) or 7 (all except debugging messages), respectively.       Every text line in a message has  its  own  loglevel.   This  level  is       DEFAULT_MESSAGE_LOGLEVEL  - 1 (6) unless the line starts with <d> where       d is a digit in the range 1-7, in which case the level is d.  The  con‐       ventional  meaning  of  the  loglevel is defined in <linux/kernel.h> as       follows:       #define KERN_EMERG    "<0>"  /* system is unusable               */       #define KERN_ALERT    "<1>"  /* action must be taken immediately */       #define KERN_CRIT     "<2>"  /* critical conditions              */       #define KERN_ERR      "<3>"  /* error conditions                 */       #define KERN_WARNING  "<4>"  /* warning conditions               */       #define KERN_NOTICE   "<5>"  /* normal but significant condition */       #define KERN_INFO     "<6>"  /* informational                    */       #define KERN_DEBUG    "<7>"  /* debug-level messages             */RETURN VALUE       For type equal to 2, 3, or 4, a successful call to syslog() returns the       number of bytes read.  For type 9, syslog() returns the number of bytes       currently available to be read on the kernel log buffer.  For type  10,       syslog() returns the total size  of  the  kernel log buffer.  For other       values of type, 0 is returned on success.       In case of error, -1 is returned, and errno  is  set  to  indicate  the       error.ERRORS       EINVAL Bad  parameters  (e.g., bad type; or for type 2, 3, or 4, buf is              NULL, or len is less than zero; or for type 8, the level is out‐              side the range 1 to 8).       EPERM  An attempt was made to change console_loglevel or clear the ker‐              nel message ring buffer by a process  without  root  permissions              (more precisely: without the CAP_SYS_ADMIN capability).       ERESTARTSYS              System  call  was  interrupted  by  a  signal; nothing was read.              (This can be seen only during a trace.)CONFORMING TO       This system call is Linux-specific and should not be used  in  programs       intended to be portable.NOTES       From  the  very start people noted that it is unfortunate that a system       call and a library routine of the same name are entirely different ani‐       mals.   In  libc4  and  libc5  the  number  of this call was defined by       SYS_klog.  In glibc 2.0 the syscall is baptized klogctl().SEE ALSO       syslog(3)COLOPHON       This page is part of release 2.79 of the Linux  man-pages  project.   A       description  of  the project, and information about reporting bugs, can       be found at http://www.kernel.org/doc/man-pages/.

 

您好,我是为向您求教才注册的。我现在想以动态分配设备号的方式加载驱动,并建立设备节点。由于是新手,所以好多地方不是很明白。总之,我希望能在驱动加载上就自动建立设备文件节点,方法1:在网上看到好像说是在驱动中用class之类的可以达到这个效果;方法2:希望通过读取/proc/dev得到的消息来建立设备文件节点,但没写过shell脚本,且不知道怎样让它自动运行。希望得到您的提示。谢谢


要自动创建节点的话必须实现:
1、文件系统中mdev或udev
2、驱动中实现class(或是用偷懒点的办法:直接注册misc类)。

 

 

 

 

刚才申请的帐号居然登录不上,所以只能匿名了,郁闷……

 

博主,请问一下你的hello模块没什么没写Kconfig文件,我看有的书上说添加模块必须写kconfig文件啊····?请指教,谢谢!


我是在内核外编译模块,不用Kconfig

 

 

 

 

请问楼主你的程序哪里来的,书上都不全呀?


我的程序是根据《LDD3》的程序修改、增删而成的。 

 

 

 

 

请问测试程序read(sculltest , &buffer2[20-i] , i),write(sculltest , &buffer1[20-i] , i)两个函数与驱动是怎么挂钩,是怎么样一个运行机制?谢谢。

 

哎……驱动层与用户层的,代码上的联系是怎么样的?说实话,这个驱动我看完了都不知道它能做什么?更不知道测试程序应该怎么写...惭愧啊


所以在学驱动前一定要学一点应用层的东西,写一些应用层的小软件试试,不如驱动是学不下去的。

 

 

 

 

请问你当初在应用层是如何学习的?谢谢指点。

 

我看到你也是硬件出身,很期待你对于应用层开发入门的建议。


我是初学者,我按照你提供的程序输入make modules 输出提示:missing separator. Stop.

 

不管读,还是写,都休眠,然后就卡在那了,动也动不了,大概是什么原因

 

博主好久没发 新的关于驱动的啦 ,终于来啦 哈哈,激动

 


博主:      我是初学者,写2440的linux驱动时会用到一些例如s3c2410_gpio_setpin()的函数,这些函数的定义在内核的哪个目录下呢?有没有一些系统的函数用法说明啊?




博主:请教个问题,你在驱动程序IO_irq.c中的函数为int IO_irq_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)而你在应用程序IO_irq_test.c中调用的if ((code=ioctl( IO_irqtest , IO_IRQ_1  , NULL ) ) < 0) ,没有参数struct inode *inode,能解释下吗?谢谢

 

哈哈,最近也在学ldd3,每学完一章就来你这儿看一下你的笔记,实在是写得太好了。

 





我刚学习linux驱动,请问以下问题怎么解决呢?[root@localhost LinuxDriver]# insmod ./hello.o./hello.o: kernel-module version mismatch        ./hello.o was compiled for kernel version 2.4.20        while this kernel is version 2.4.20-8.


你编译模块使用的内核版本和运行模块的内核版本不同,建议统一下。
或者,好像在内核配置上可以关闭这个检查。

 

 

 

 

喜欢注释  感谢一个。。。。

 

牛人,兄弟你太有才了,前途无量!

 

总结的不错....转了....谢谢!

 

拜读美文,醍醐灌顶,如梦初醒。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值